diff options
Diffstat (limited to '')
305 files changed, 104639 insertions, 0 deletions
diff --git a/Bonus/README b/Bonus/README new file mode 100644 index 0000000..1e3a0de --- /dev/null +++ b/Bonus/README @@ -0,0 +1,48 @@ +html2latex + + HTML��ʸ���LaTeX ���Ѵ����ޤ���Ruby������ץȤǤ����Դ����Ǥ��� + �������٤���ˤ�Ω�Ĥ��⤷��ޤ��� + + ����ˡ + + html2latex file.html > file.tex + + �������֤��Ƥ�����ͳ + + makeref �Τ�������ʼ��Ǥ� :-) + +makeref + + HTML��ʸ����ɤߡ��������ֹ��ޤ����ֹ�ä�ʸ��� + ɸ����Ϥ˽Ф����Ǹ�ˤ��ΰ�������Ϥ��ޤ���Ruby ������ץȤǤ��� + + ����ˡ + + makeref [-url base_url] [file] + + -url: ʸ���URL����ꤷ�ޤ�����ΰ�����Ф��Ȥ��ˡ�����URL�� + �䴰���뤿��˻Ȥ��ޤ��� + + �Х� + + HTML�ε��ҥߥ�( < �ǤϤʤ� < ��Ȥ���&... �κǸ�� ; ���դ��ʤ� + ��)������ȡ��Ỵ�ʷ�̤ˤʤ뤳�Ȥ�����ޤ��� + + Ⱦ�ѥ���(JIS X-0201����)���б����Ƥ��ޤ��� + +htmldump + + URL ����HTMLʸ����ɤߡ��������ֹ�ä���������ɸ����Ϥ� + �Ф��ޤ��� + + ����ˡ + + dumphtml [URL] + + URL ���ά����ȡ�$WWW_HOME �����Ƥ��ɤߤޤ��� + + �Х� + + URL �λؤ�ʸ��HTML�Ǥʤ��ä���硤���襤�����ʤ��Ȥˤʤ�ޤ��� + makeref ��ȤäƤ���Τǡ�makeref �����ޤ������Ǥ��ʤ�ʸ���ɽ�� + ���Ѥˤʤ�ޤ��� diff --git a/Bonus/README.eng b/Bonus/README.eng new file mode 100644 index 0000000..06613ee --- /dev/null +++ b/Bonus/README.eng @@ -0,0 +1,49 @@ +html2latex + + Convert HTML document into LaTeX. Ruby script. incomplete. + + Usage: + + html2latex file.html > file.tex + + Why this script is here? + + To exploit code for makeref. :-) + +makeref + + Read HTML document and number the anchors. Print numbered document + into standard output and append reference index. Ruby script. + + Usage: + + makeref [-u] [-url base_url] [file] + + -url: Specify URL of the document. It is used to complete link + in the document. + + -u: Append URL after each anchor, instead of reference number. + + Bugs + + If there are any error in HTML (unbalanced < , character entity + without ; , etc.), output will be miserable. + +htmldump + + Read HTML document from URL, number the anchors and format it, + and output it on standard output. + + Usage + + htmldump [-u] [URL] + + -u: Append URL after each anchor, instead of reference number. + + If URL is omitted, $WWW_HOME is used instead. + + Bugs + + It assumes that the document on URL is HTML. + As it uses makeref to number the anchor, it can't handle any document + makeref can't handle. diff --git a/Bonus/html2latex b/Bonus/html2latex new file mode 100755 index 0000000..7b894e7 --- /dev/null +++ b/Bonus/html2latex @@ -0,0 +1,517 @@ +#!/usr/local/bin/ruby + +# +# HTML to LaTeX converter +# by A. Ito, 16 June, 1997 +# + +require 'kconv' + +# configuration +def gif2eps(giffile,epsfile) + cmd = "convert #{giffile} #{epsfile}" + STDERR.print cmd,"\n" + system cmd +end + +########################################################################### +class Tag + def initialize(str) + if str =~ /<(.+)>/ then + str = $1 + end + tags = str.split + @tagname = tags.shift.downcase + @vals = {} + tags.each do |t| + if t =~ /=/ then + tn,tv = t.split(/\s*=\s*/,2) + tv.sub!(/^"/,"") + tv.sub!(/"$/,"") + @vals[tn.downcase] = tv + else + @vals[t.downcase] = TRUE + end + end + end + def tagname + return @tagname + end + def each + @vals.each do |k,v| + yield k,v + end + end + def switch(k) + return @vals[k] + end +end + +class TokenStream + TAG_START = ?< + TAG_END = ?> + AMP_START = ?& + AMP_END = ?; + + AMP_REPLACE_TABLE = { + '&' => '\\&', + '>' => '$>$', + '<' => '$<$', + ' ' => '~', + '"' => '"', + } + def initialize(file) + if file.kind_of?(File) then + @f = file + else + @f = File.new(file) + end + @buf = nil + @bpos = 0 + end + + def read_until(endsym) + complete = FALSE + tag = [] + begin + while @bpos < @buf.size + c = @buf[@bpos] + if c == endsym then + tag.push(c.chr) + complete = TRUE + @bpos += 1 + break + end + if c == 10 || c == 13 then + tag.push(' ') + else + tag.push(c.chr) + end + @bpos += 1 + end + unless complete + @buf = @f.gets + @bpos = 0 + break if @f.eof? + end + end until complete + return tag.join('') + end + + def get + while TRUE + if @buf.nil? then + @buf = Kconv.toeuc(@f.gets) + if @f.eof? then + return nil + end + @bpos = 0 + end + if @buf[@bpos] == TAG_START then + return Tag.new(read_until(TAG_END)) + elsif @buf[@bpos] == AMP_START then + return replace_amp(read_until(AMP_END)) + else + i = @bpos + while i < @buf.size && @buf[i] != TAG_START && @buf[i] != AMP_START + i += 1 + end + r = @buf[@bpos,i-@bpos] + if i == @buf.size then + @buf = nil + else + @bpos = i + end + redo if r =~ /^\s+$/ + return r + end + end + end + public :eof? + def eof? + @f.eof? + end + def replace_amp(s) + if AMP_REPLACE_TABLE.key?(s) then + return AMP_REPLACE_TABLE[s] + else + return s + end + end +end + + +def print_header + print ' +\documentstyle[epsf]{jarticle} +\def\hr{\par\hbox to \textwidth{\hrulefill}} +\def\pre{\begin{quote}\def\baselinestretch{0.8}\tt\obeylines} +\def\endpre{\end{quote}} +\makeatletter +\@ifundefined{gt}{\let\gt=\dg}{} +\makeatother +' +end + + +class Environ_stack + def initialize(*envs) + @stack = envs + end + def action(tag) + if tag =~ /^!/ then # comment + return ["",nil] + end + i = @stack.size-1 + while i >= 0 + a = @stack[i].action(tag) + unless a.nil? then + return a + end + i -= 1 + end + return nil + end + def pop + @stack.pop + end + def push(env) + @stack.push(env) + end + def top + @stack[@stack.size-1] + end + def dup + @stack.push(top.clone) + end +end + + +class Environment + def initialize(interp) + @silent = FALSE + @in_table = FALSE + @interp = interp; + @align = nil; + end + def action(tag) + return @interp[tag] + end + + def flush(tok) + if tok.kind_of?(String) then + tok = tok.gsub(/&/,"\\&"); + tok = tok.gsub(/%/,"\\%"); + tok = tok.gsub(/#/,"\\#"); + tok = tok.gsub(/\$/,"\\$"); + tok = tok.gsub(/_/,"\\verb+_+"); + tok = tok.gsub(/\^/,"\\verb+^+"); + tok = tok.gsub(/~/,"\\verb+~+"); + end + if @in_table then + @table[@table_rows][@table_cols] += tok + elsif !@silent then + if !@align.nil? && tok =~ /\n$/ then + print tok.chop,"\\\\\n" + else + print tok + end + end + end + + def set_interp(interp) + @interp = interp + end + + # tag processing methods + + # <TITLE> + def do_silent(tag) + @silent = TRUE + end + + # </TITLE> + def undo_silent(tag) + @silent = FALSE + end + + # <IMG> + def img_proc(tag) + src = tag.switch('src') + newfile = src.sub(/\.GIF/i,".eps") + gif2eps(src,newfile) + flush "\\epsfile{file=#{newfile}}\n" + end + + # <TABLE> + def starttable(tag) + @table = [] + @tablespan = [] + @table_rows = -1 + @table_cols_max = 0 + @in_table = TRUE + unless tag.switch('border').nil? then + @table_border = TRUE + else + @table_border = FALSE + end + end + + # <TR> + def start_row(tag) + @table_rows += 1 + @table[@table_rows] = [] + @tablespan[@table_rows] = [] + @table_cols = -1 + @colspan = 1 + end + + # <TD> + def start_col(tag) + @colspan = tag.switch('colspan') + if @colspan.nil? then + @colspan = 1 + else + @colspan = @colspan.to_i + end + @tablespan[@table_rows][@table_cols+1] = @colspan + @table_cols += @colspan + if @table_cols > @table_cols_max then + @table_cols_max = @table_cols + end + end + + # </TABLE> + def endtable(tag) + @in_table = FALSE + flush "\\begin{tabular}{*{" + flush @table_cols_max+1 + if @table_border then + flush "}{|l}|}\n\\hline\n" + else + flush "}{l}}\n" + end + for i in 0..@table_rows + j = 0 + while j <= @table_cols + span = @tablespan[i][j] + if span == 1 then + flush @table[i][j] + elsif @table_border then + form = "|l" + if j+span > @table_cols then + form = "|l|" + end + flush "\\multicolumn{"+span.to_s+"}{"+form+"}{" + flush @table[i][j+span-1] + flush "}" + else + flush "\\multicolumn{"+span.to_s+"}{l}{" + flush @table[i][j+span-1] + flush "}" + end + j += span + if j <= @table_cols then + flush "&" + end + end + flush "\\\\\n" + flush "\\hline\n" if @table_border + end + flush "\\end{tabular}\n" + end + + # <CENTER> + def startcenter(tag) + if @in_table then + flush "\\hfil" + else + flush "\\begin{center}\n" + end + end + + # </CENTER> + def endcenter(tag) + if @in_table then + flush "\\hfil" + else + flush "\\end{center}\n" + end + end + + # <P> + def paragraph(tag) + align = tag.switch('align') + if align.nil? then + flush "\\par\n" + @endparagraph = "" + else + align = align.downcase + case align + when "left" then + flush "\\begin{flushleft}\n" + @endparagraph = "\\end{flushleft}\n" + when "center" then + flush "\\begin{center}\n" + @endparagraph = "\\end{center}\n" + when "right" then + flush "\\begin{flushright}\n" + @endparagraph = "\\end{flushright}\n" + end + end + @align = align + end + + # </P> + def endparagraph(tag) + unless @align.nil? then + @align = nil + flush @endparagraph + end + end +end + + +enum_interp = { + 'li' => ["\\item ",nil] +} + +item_interp = { + 'li' => ["\\item ",nil] +} + +desc_interp = { + 'dt' => ["\\item[",nil], + 'dd' => ["]\n",nil] +} + +table_interp = { + 'tr' => [:start_row,nil], + 'td' => [:start_col,nil], + '/tr' => ["",nil], + '/td' => ["",nil], +} + +para_interp = { + '/p' => [:endparagraph ,"pop",TRUE], +} + +main_interp = { + 'body' => ["\\begin{document}\n",nil,FALSE], + '/body' => ["\\end{document}\n",nil,FALSE], + 'head' => ["",nil,FALSE], + '/head' => ["",nil,FALSE], + 'html' => ["",nil,FALSE], + '/html' => ["",nil,FALSE], + 'title' => [:do_silent,nil,FALSE], + '/title' => [:undo_silent,nil,FALSE], + '!' => ["",nil,FALSE], + 'h1' => ["\\section{",nil,TRUE], + 'h2' => ["\\subsection{",nil,TRUE], + 'h3' => ["\\subsubsection{",nil,TRUE], + 'h4' => ["\\paragraph{",nil,TRUE], + '/h1' => ["}\n",nil,TRUE], + '/h2' => ["}\n",nil,TRUE], + '/h3' => ["}\n",nil,TRUE], + '/h4' => ["}\n",nil,TRUE], + 'a' => ["",nil,TRUE], + '/a' => ["",nil,TRUE], + 'center' => [:startcenter,nil,TRUE], + '/center' => [:endcenter,nil,TRUE], + 'ol' => ["\\begin{enumerate}\n",enum_interp,TRUE], + '/ol' => ["\\end{enumerate}\n","pop",TRUE], + 'ul' => ["\\begin{itemize}\n",item_interp,TRUE], + '/ul' => ["\\end{itemize}\n","pop",TRUE], + 'dl' => ["\\begin{description}\n",desc_interp,TRUE], + '/dl' => ["\\end{description}\n","pop",TRUE], + 'pre' => ["\\begin{pre}\n",nil,TRUE], + '/pre' => ["\\end{pre}\n",nil,TRUE], + 'p' => [:paragraph ,para_interp,TRUE], + 'br' => ["\\par ",nil,TRUE], + 'img' => [:img_proc,nil,TRUE], + 'hr' => ["\\hr ",nil,TRUE], + 'b' => ["{\\bf\\gt ",nil,TRUE], + '/b' => ["}",nil,TRUE], + 'strong' => ["{\\bf\\gt ",nil,TRUE], + '/strong' => ["}",nil,TRUE], + 'dfn' => ["{\\bf\\gt ",nil,TRUE], + '/dfn' => ["}",nil,TRUE], + 'i' => ["{\\it",nil,TRUE], + '/i' => ["}",nil,TRUE], + 'address' => ["{\\it",nil,TRUE], + '/address'=> ["}",nil,TRUE], + 'cite' => ["{\\it",nil,TRUE], + '/cite' => ["}",nil,TRUE], + 'code' => ["{\\tt",nil,TRUE], + '/code' => ["}",nil,TRUE], + 'kbd' => ["{\\tt",nil,TRUE], + '/kbd' => ["}",nil,TRUE], + 'tt' => ["{\\tt",nil,TRUE], + '/tt' => ["}",nil,TRUE], + 'samp' => ["{\\tt",nil,TRUE], + '/samp' => ["}",nil,TRUE], + 'em' => ["{\\em",nil,TRUE], + '/em' => ["}",nil,TRUE], + 'u' => ["$\\underline{\\mbox{",nil,TRUE], + '/u' => ["}}$",nil,TRUE], + 'sub' => ["${}_\mbox{",nil,TRUE], + '/sub' => ["}$",nil,TRUE], + 'sup' => ["${}^\mbox{",nil,TRUE], + '/sup' => ["}$",nil,TRUE], + 'table' => [:starttable, table_interp,TRUE], + '/table' => [:endtable, "pop",TRUE], + 'font' => ["",nil,TRUE], + '/font' => ["",nil,TRUE], +} + + + + +################################ MAIN #################################### + +$in_document = FALSE +print_header +intp = Environ_stack.new(Environment.new(main_interp)) +f = TokenStream.new(ARGV[0]) +until f.eof? + tok = f.get + if tok.kind_of?(Tag) then + case tok.tagname + when "body" + $in_document = TRUE + when "/body" + $in_document = FALSE + end + act = intp.action(tok.tagname) + if act.nil? then + STDERR.print "tag ",tok.tagname," ignored\n" + else + if act[2] && !$in_document then + print "\\begin{document}\n" + $in_document = TRUE + end + # enviconment push + if act[1].kind_of?(Hash) && + (tok.tagname != "p" || tok.switch('align') != nil) then + intp.dup + intp.top.set_interp(act[1]) + end + + if act[0].kind_of?(String) then + intp.top.flush act[0] + elsif act[0].kind_of?(Fixnum) then # interned symbol + intp.top.send(act[0],tok) + end + + # environment pop + if act[1] == "pop" then + intp.pop + end + end + elsif !tok.nil? then + intp.top.flush tok + end +end +if $in_document then + print "\\end{document}\n" +end diff --git a/Bonus/htmldump b/Bonus/htmldump new file mode 100755 index 0000000..4be60bf --- /dev/null +++ b/Bonus/htmldump @@ -0,0 +1,12 @@ +#!/bin/sh +OPT= +if [ $# -gt 0 -a $1 = "-u" ]; then + OPT=-u + shift +fi +if [ $# = 0 ]; then + URL=$WWW_HOME +else + URL=$1 +fi +w3m -dump_source $URL | makeref $OPT -url $URL | w3m -dump -F -T text/html diff --git a/Bonus/makeref b/Bonus/makeref new file mode 100755 index 0000000..9cb1942 --- /dev/null +++ b/Bonus/makeref @@ -0,0 +1,266 @@ +#!/usr/local/bin/ruby + +# HTML reference generator +# by A.Ito 1999/3/30 + +require 'kconv' + +########################################################################### +class URL + attr 'scheme' + attr 'host' + attr 'port' + attr 'file' + attr 'label' + def initialize(str) + if /([a-zA-Z+\-]+):(.*)/ =~ str then + @scheme = $1 + str = $2 + else + @scheme = 'unknown' + end + hostpart = '' + if %r'//([^/]*)(/.*)' =~ str then + hostpart = $1 + str = $2 + elsif %r'//([^/]*)$' =~ str then + hostpart = str + str = '' + end + if hostpart != '' then + if /(.*):(\d+)/ =~ hostpart then + @host = $1 + @port = $2 + else + @host = hostpart + @port = '' + end + else + @host = @port = '' + end + if /(.*)#(.*)/ =~ str then + @file = $1 + @label = $2 + else + @file = str + @label = '' + end + end + def to_s + s = "#{@scheme}:" + if s == 'news' or s == 'mailto' then + return s+@file + end + s += "//"+@host + s += ":"+@port if @port.size > 0 + s += @file + s += "#"+@label if @label.size > 0 + s + end + def complete(current) + @scheme = current.scheme if @scheme == 'unknown' + @port = current.port if @host == '' and @port == '' + @host = current.host if @host == '' + unless @file =~ %r'^/' then + @file = File.expand_path(File.dirname(current.file)+'/'+@file) + end + self + end +end + +class Tag + def initialize(str) + if str =~ /<(.+)>/ then + str = $1 + end + tags = str.split + @tagname = tags.shift.downcase + @vals = {} + tags.each do |t| + if t =~ /=/ then + tn,tv = t.split(/\s*=\s*/,2) + tv.sub!(/^"/,"") + tv.sub!(/"$/,"") + @vals[tn.downcase] = tv + else + @vals[t.downcase] = TRUE + end + end + end + def tagname + return @tagname + end + def each + @vals.each do |k,v| + yield k,v + end + end + def switch(k) + return @vals[k] + end + def to_s + if tagname =~ /!--/ then + return '' + end + t = "<"+tagname + if @vals.size == 0 then + return t+">" + end + each do |a,v| + if v == true then + t += " #{a}" + else + t += " #{a}=\"#{v}\"" + end + end + t+">" + end +end + +class TokenStream + TAG_START = ?< + TAG_END = ?> + AMP_START = ?& + AMP_END = ?; + + def initialize(file) + if file.kind_of?(IO) then + @f = file + else + @f = File.new(file) + end + @buf = nil + @bpos = 0 + end + + def read_until(endsym) + complete = FALSE + tag = [] + begin + while @bpos < @buf.size + c = @buf[@bpos] + if c == endsym then + tag.push(c.chr) + complete = TRUE + @bpos += 1 + break + end + if c == 10 || c == 13 then + tag.push(' ') + else + tag.push(c.chr) + end + @bpos += 1 + end + unless complete + @buf = @f.gets + @bpos = 0 + break if @f.eof? + end + end until complete + return tag.join('') + end + + def get + while TRUE + if @buf.nil? then + @buf = @f.gets + if @f.eof? then + return nil + end + @buf = Kconv.toeuc(@buf) + @bpos = 0 + end + if @buf[@bpos] == TAG_START then + return Tag.new(read_until(TAG_END)) + elsif @buf[@bpos] == AMP_START then + return read_until(AMP_END) + else + i = @bpos + while i < @buf.size && @buf[i] != TAG_START && @buf[i] != AMP_START + i += 1 + end + r = @buf[@bpos,i-@bpos] + if i == @buf.size then + @buf = nil + else + @bpos = i + end + redo if r =~ /^\s+$/ + return r + end + end + end + public :eof? + def eof? + @f.eof? + end +end + +################################ MAIN #################################### + +refs = [] +refnum = 0 +body_finished = false +html_finished = false +currentURL = nil +immediate_ref = false + +while ARGV[0] =~ /^-/ + case ARGV.shift + when '-url' + currentURL = URL.new(ARGV.shift) + when '-u' + immediate_ref = true + end +end + +if ARGV.size > 0 then + f = TokenStream.new(ARGV[0]) +else + f = TokenStream.new(STDIN) +end + +until f.eof? + tok = f.get + if tok.kind_of?(Tag) then + if tok.tagname == 'a' and !tok.switch('href').nil? then + refs[refnum] = tok.switch('href') + refnum += 1 + elsif tok.tagname == '/a' then + if immediate_ref then + r = refs[refnum-1] + if !currentURL.nil? then + r = URL.new(r).complete(currentURL).to_s + end + print "[#{r}]" + else + print "[#{refnum}]" + end + elsif tok.tagname == '/body' then + body_finished = true + break + elsif tok.tagname == '/html' then + html_finished = true + break + end + print tok.to_s + elsif !tok.nil? then + print tok + end +end +if !immediate_ref and refs.size > 0 then + print "<hr><h2>References</h2>\n" + for i in 0..refs.size-1 + if currentURL.nil? then + r = refs[i] + else + r = URL.new(refs[i]) + r.complete(currentURL) + r = r.to_s + end + print "[#{i+1}] #{r}<br>\n" + end +end +print "</body>\n" unless body_finished +print "</html>\n" unless html_finished diff --git a/Bonus/scanhist.rb b/Bonus/scanhist.rb new file mode 100644 index 0000000..69dcc9d --- /dev/null +++ b/Bonus/scanhist.rb @@ -0,0 +1,88 @@ +#!/usr/local/bin/ruby + +# scan history + +def usage + STDERR.print "usage: scanhist -h HISTORY ML-archive1 ML-archive2 ...\n" + exit 1 +end + +def html_quote(s) + s.gsub!(/&/,"&") + s.gsub!(/</,"<") + s.gsub!(/>/,">") + s +end + +if ARGV.size == 0 then + usage +end + +histfile = nil + +while ARGV[0] =~ /^-/ + case ARGV.shift + when "-h" + histfile = ARGV.shift + else + usage + end +end + +if histfile.nil? then + usage +end + +patched = {} +histline = {} +f = open(histfile) +while f.gets + if /Subject: (\[w3m-dev.*\])/ then + patched[$1] = true + histline[$1] = $. + end +end +f.close + +archive = {} +subject = nil +for fn in ARGV + f = open(fn) + while f.gets + if /^From / then + # beginning of a mail + subject = nil + elsif subject.nil? and /^Subject: / then + $_ =~ /Subject: (\[w3m-dev.*\])/ + subject = $1 + archive[subject] = [$_.chop.sub(/^Subject:\s*/,""),false,fn+"#"+($.).to_s] + elsif /^\+\+\+/ or /\*\*\*/ or /filename=.*(patch|diff).*/ or /^begin \d\d\d/ + archive[subject][1] = true + end + end + f.close +end + +print "<html><head><title>w3m patch configuration\n</title></head><body>\n" +print "<pre>\n" +for sub in archive.keys.sort + a = archive[sub] + if a[1] then + if patched[sub] then + print "[<a href=\"#{histfile}\##{histline[sub]}\">+</a>]" + else + print "[-]" + end + print "<a href=\"#{a[2]}\">" + print "<b>",html_quote(a[0]),"</b></a>\n" + else + if patched[sub] then + print "[<a href=\"#{histfile}\##{histline[sub]}\">o</a>]" + else + print " " + end + print "<a href=\"#{a[2]}\">" + print "<b>",html_quote(a[0]),"</b></a>\n" + end +end +print "</pre></body></html>\n" diff --git a/Bonus/wrap3m b/Bonus/wrap3m new file mode 100755 index 0000000..9555c9a --- /dev/null +++ b/Bonus/wrap3m @@ -0,0 +1,33 @@ +#!/bin/sh + +HOMEPAGE=http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/ +OPT="" +URL="" +for i in $@ +do + case $i in + -*) + OPT="$OPT $i" + ;; + *) + URL="$URL $i" + ;; + esac +done + +if [ -z "$URL" ]; then + URL=$HOMEPAGE +fi +URLARG="" +for u in $URL +do + if [ `expr $u : '[a-z][a-z]*://'` -gt 0 ]; then + URLARG="$URLARG $u" + elif [ -f $u -o -d $u ]; then + URLARG="$URLARG $u" + else + URLARG="$URLARG http://$u" + fi +done + +w3m $OPTS $URLARG diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f756399 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +GCLIBTGZ=gc5.0alpha3.tar.gz + +all: XXMakefile + make -f XXMakefile + +install: XXMakefile + make -f XXMakefile install + +uninstall: XXMakefile + make -f XXMakefile uninstall + +proto: XXMakefile + make -f XXMakefile proto + +clean: XXMakefile + make -f XXMakefile clean + +sweep: XXMakefile + make -f XXMakefile sweep + +veryclean: clean sweep + rm XXMakefile + (cd gc; make clean) + rm -f config.param + rm -f */*~ */*.orig */*.rej + +prepare: + rm -rf gc + gzip -dc ../$(GCLIBTGZ) | tar xvf - + cp XMakefile.dist XMakefile + +dist: XXMakefile + make -f XXMakefile dist + +bindist: XXMakefile + make -f XXMakefile bindist + +indent: + indent -orig -nce -ncdb -i4 -di1 -nbc *.c *.h + +XXMakefile: XMakefile config.h + awk '/^#ifdef makefile_parameter/,/^#else/' config.h | cat - XMakefile > XXMakefile diff --git a/Patches/alpha b/Patches/alpha new file mode 100644 index 0000000..422cd26 --- /dev/null +++ b/Patches/alpha @@ -0,0 +1,19 @@ +*** gc/Makefile.orig Thu Jun 24 10:08:17 1999 +--- gc/Makefile Thu Jun 24 10:08:54 1999 +*************** +*** 10,16 **** + ABI_FLAG= + CC=cc $(ABI_FLAG) + CXX=CC $(ABI_FLAG) +! AS=as $(ABI_FLAG) + # The above doesn't work with gas, which doesn't run cpp. + # Define AS as `gcc -c -x assembler-with-cpp' instead. + # Under Irix 6, you will have to specify the ABI (-o32, -n32, or -64) +--- 10,16 ---- + ABI_FLAG= + CC=cc $(ABI_FLAG) + CXX=CC $(ABI_FLAG) +! AS=gcc -c -x assembler-with-cpp + # The above doesn't work with gas, which doesn't run cpp. + # Define AS as `gcc -c -x assembler-with-cpp' instead. + # Under Irix 6, you will have to specify the ABI (-o32, -n32, or -64) diff --git a/Patches/armlinux b/Patches/armlinux new file mode 100644 index 0000000..18dd202 --- /dev/null +++ b/Patches/armlinux @@ -0,0 +1,110 @@ +From lars@junk.nocrew.org Tue Mar 7 04:44 EST 2000 +Return-Path: <lars@junk.nocrew.org> +Received: from ei5sun.yz.yamagata-u.ac.jp (ei5sun.yz.yamagata-u.ac.jp [133.24.114.42]) + by ei5hp710.yz.yamagata-u.ac.jp (8.9.3/8.9.3) with ESMTP id EAA25953 + for <aito@ei5hp710.yz.yamagata-u.ac.jp>; Tue, 7 Mar 2000 04:44:51 -0500 (EST) +Received: from junk.nocrew.org (mail@[212.73.17.42]) by ei5sun.yz.yamagata-u.ac.jp (8.8.0/3.5Wbeta) with ESMTP id SAA07952 for <aito@ei5sun.yz.yamagata-u.ac.jp>; Tue, 7 Mar 2000 18:54:43 +0900 (JST) +Received: from lars by junk.nocrew.org with local (Exim 3.03 #1 (Debian)) + for aito@ei5sun.yz.yamagata-u.ac.jp + id 12SGVE-0001rh-00; Tue, 07 Mar 2000 10:42:08 +0100 +To: aito@ei5sun.yz.yamagata-u.ac.jp +Subject: ARMLinux patch +From: lars brinkhoff <lars@nocrew.org> +Date: 07 Mar 2000 10:42:08 +0100 +Message-ID: <85zosbjevj.fsf@junk.nocrew.org> +Lines: 89 +User-Agent: Gnus/5.0803 (Gnus v5.8.3) Emacs/20.5 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Sender: lars brinkhoff <lars@junk.nocrew.org> + +This patch is an attempt to make w3m version 0.1.6 work in ARMLinux. +It seems to work well. + +--- gc/gcconfig.h.org Wed Jan 12 05:23:37 2000 ++++ gc/gcconfig.h Sun Mar 5 14:24:52 2000 +@@ -151,6 +151,10 @@ + # define SPARC + # define mach_type_known + # endif ++# if defined(LINUX) && (defined(__arm__) || defined(arm)) ++# define ARM ++# define mach_type_known ++# endif + # if defined(__alpha) || defined(__alpha__) + # define ALPHA + # if !defined(LINUX) +@@ -985,6 +989,39 @@ + # define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) + # define DATAEND (&_end) + # define HEURISTIC2 ++# endif ++ ++# ifdef ARM ++# define MACH_TYPE "ARM" ++# ifdef LINUX ++# define OS_TYPE "LINUX" ++# define HEURISTIC1 ++# define STACKBOTTOM ((ptr_t) 0xbffffffc) ++# ifdef __ELF__ ++# define DYNAMIC_LOADING ++# include <features.h> ++# if defined(__GLIBC__) && __GLIBC__ >= 2 ++ extern int __data_start; ++# define DATASTART ((ptr_t)(&__data_start)) ++# else ++ extern char **__environ; ++# define DATASTART ((ptr_t)(&__environ)) ++ /* hideous kludge: __environ is the first */ ++ /* word in crt0.o, and delimits the start */ ++ /* of the data segment, no matter which */ ++ /* ld options were passed through. */ ++ /* We could use _etext instead, but that */ ++ /* would include .rodata, which may */ ++ /* contain large read-only data tables */ ++ /* that we'd rather not scan. */ ++# endif ++ extern int _end; ++# define DATAEND (&_end) ++# else ++ ARMLinux non elf ? ++# endif ++# endif ++# define ALIGNMENT 4 + # endif + + # ifndef STACK_GROWS_UP +diff -ur gc/mach_dep.c w3m-0.1.6.lars/gc/mach_dep.c +--- gc/mach_dep.c.org Wed Jan 12 05:23:37 2000 ++++ gc/mach_dep.c Thu Jan 27 21:28:39 2000 +@@ -337,7 +337,7 @@ + /* other machines... */ + # if !(defined M68K) && !(defined VAX) && !(defined RT) + # if !(defined SPARC) && !(defined I386) && !(defined NS32K) +-# if !defined(POWERPC) && !defined(UTS4) ++# if !defined(POWERPC) && !defined(UTS4) && !defined(ARM) + --> bad news <-- + # endif + # endif +diff -ur w3m/gc/os_dep.c w3m-0.1.6.lars/gc/os_dep.c +--- gc/os_dep.c.org Wed Jan 12 05:23:37 2000 ++++ gc/os_dep.c Thu Jan 27 21:37:27 2000 +@@ -72,7 +72,7 @@ + # define NEED_FIND_LIMIT + # endif + +-# if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA)) ++# if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA) || defined (ARM)) + # define NEED_FIND_LIMIT + # endif + +@@ -139,7 +139,7 @@ + # define OPT_PROT_EXEC 0 + #endif + +-#if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA)) ++#if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA) || defined(ARM)) + /* The I386 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ + diff --git a/Patches/ews4800 b/Patches/ews4800 new file mode 100644 index 0000000..954523a --- /dev/null +++ b/Patches/ews4800 @@ -0,0 +1,34 @@ +--- XMakefile.orig Mon Mar 19 10:47:56 2001 ++++ XMakefile Mon Mar 19 21:03:34 2001 +@@ -65,7 +65,7 @@ + $(CC) $(CFLAGS) -o $(HELPER) w3mhelperpanel.o $(LIBS) + + gc/gc.a: +- cd gc; make CC='$(CC)' CFLAGS='$(GCCFLAGS)' ++ cd gc; make CC='$(CC)' CFLAGS='$(GCCFLAGS) -Dmips' + + install: $(TARGETS) + -$(MKDIR) $(DESTDIR)$(BIN_DIR) +--- gc/Makefile.orig Tue Jan 4 14:46:50 2000 ++++ gc/Makefile Thu Mar 22 18:10:10 2001 +@@ -7,7 +7,7 @@ + # and runs some tests of collector and cords. Does not add cords or + # c++ interface to gc.a + # cord/de - builds dumb editor based on cords. +-ABI_FLAG= ++ABI_FLAG=-Kconform_pic + CC=cc $(ABI_FLAG) + CXX=CC $(ABI_FLAG) + AS=as $(ABI_FLAG) +--- gc/gcconfig.h.orig Tue Jun 6 14:39:29 2000 ++++ gc/gcconfig.h Mon Mar 19 21:02:51 2001 +@@ -64,7 +64,8 @@ + # endif + # if defined(mips) || defined(__mips) + # define MIPS +-# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) ++# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) ||\ ++ defined(nec_ews) + # define ULTRIX + # else + # if !defined(LINUX) diff --git a/Patches/hpux11 b/Patches/hpux11 new file mode 100644 index 0000000..c02893e --- /dev/null +++ b/Patches/hpux11 @@ -0,0 +1,25 @@ +Install note for HP-UX + +If you are running HP-UX 11 or later on PA-RISC 2.0, you +have to apply the following patch on GC library. This patch +is contributed by Dave Eaton <dwe@arde.com>. + +If you want to use HP C compiler, answer + + Input your favorite C-compiler. + (Default: cc) cc -Aa -D_HPUX_SOURCE + +If you use just 'cc' without options, you can't compile w3m. +If you are using gcc, no option is needed. + +---------------------------------------------------------------------- +--- gc/gcconfig.h.original Wed May 19 01:38:55 1999 ++++ gc/gcconfig.h Tue Jun 8 12:38:22 1999 +@@ -125,6 +125,7 @@ + # define mach_type_known + # endif + # if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ ++ || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) + # define HP_PA + # define mach_type_known diff --git a/Patches/linux2.2sparc b/Patches/linux2.2sparc new file mode 100644 index 0000000..3a5cd80 --- /dev/null +++ b/Patches/linux2.2sparc @@ -0,0 +1,10 @@ +--- gc/gcconfig.h.org ++++ gc/gcconfig.h +@@ -601,7 +601,7 @@ + extern int _end; + # define DATAEND (&_end) + # define SVR4 +-# define STACKBOTTOM ((ptr_t) 0xf0000000) ++# define STACKBOTTOM ((ptr_t) 0xeffff000) + # endif + # endif diff --git a/Patches/macosx b/Patches/macosx new file mode 100644 index 0000000..4d251f1 --- /dev/null +++ b/Patches/macosx @@ -0,0 +1,41 @@ +Jeroen Scheerder <J.Scheerder@cwi.nl> (by way of Jeroen Scheerder) +Subject: w3m 0.1.6 on Mac OS X +Content-Type: text/plain; charset="us-ascii" + +Hi, + +I've compiled w3m successfully on Mac OS X (DP3). I'm including +patches, mainly dealing with compiler complaints about possible use of +uninitialized data. + +It compiles with one warning, still: + +url.c:799: warning: variable `p' might be clobbered by `longjmp' or + `vfork' + + +On a related note: I had to modify the makefile; there is no `m' +library, and no `termcap' library, and they're not needed as well -- +but they will cause an ld failure, when present on the ld command line. + +Context diffs (against a 21/1/2000 0.1.6) are included, including the +(trivial) patch to make Boehm gc compile. + + + +*** gc/gcconfig.h Mon Mar 6 12:16:04 2000 +--- gc/gcconfig.h.org Wed Jan 12 04:23:37 2000 +*************** +*** 180,190 **** + # define POWERPC + # define mach_type_known + # endif +- # if defined(__APPLE__) +- # define MACOSX +- # define POWERPC +- # define mach_type_known +- # endif + # if defined(NeXT) && defined(mc68000) + # define M68K + # define NEXT +--- 180,185 ---- diff --git a/Patches/macppc b/Patches/macppc new file mode 100644 index 0000000..c52adc4 --- /dev/null +++ b/Patches/macppc @@ -0,0 +1,147 @@ +Here is a patch to port GC library to NetBSD/macppc. If you are +using NetBSD on PowerMac, apply this patch first. + +This patch is provided by K. Sunagawa (kei_sun@ba2.so-net.ne.jp). + +Index: gc/dyn_load.c +=================================================================== +RCS file: /cvsroot/w3m/gc/dyn_load.c,v +retrieving revision 1.1.1.2 +retrieving revision 1.2 +diff -u -u -r1.1.1.2 -r1.2 +--- gc/dyn_load.c 1999/06/02 19:29:13 1.1.1.2 ++++ gc/dyn_load.c 1999/06/03 12:44:49 1.2 +@@ -48,7 +48,8 @@ + #if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \ + !defined(MSWIN32) && !(defined(ALPHA) && defined(OSF1)) && \ + !defined(HP_PA) && !(defined(LINUX) && defined(__ELF__)) && \ +- !defined(RS6000) && !defined(SCO_ELF) ++ !defined(RS6000) && !defined(SCO_ELF) && \ ++ !(defined(NETBSD) && defined(POWERPC)) + --> We only know how to find data segments of dynamic libraries for the + --> above. Additional SVR4 variants might not be too + --> hard to add. +@@ -260,14 +261,22 @@ + # endif /* !USE_PROC ... */ + # endif /* SUNOS */ + +-#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) ++#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \ ++ (defined(NETBSD) && defined(POWERPC)) + + /* Dynamic loading code for Linux running ELF. Somewhat tested on + * Linux/x86, untested but hopefully should work on Linux/Alpha. + * This code was derived from the Solaris/ELF support. Thanks to + * whatever kind soul wrote that. - Patrick Bridges */ + ++#if defined(NETBSD) ++#include <sys/exec_elf.h> ++#define DT_DEBUG 21 ++#define PT_LOAD 1 ++#define PF_W 0x2 ++#else + #include <elf.h> ++#endif + #include <link.h> + + /* Newer versions of Linux/Alpha and Linux/x86 define this macro. We +Index: gc/gcconfig.h +=================================================================== +RCS file: /cvsroot/w3m/gc/gcconfig.h,v +retrieving revision 1.1.1.2 +retrieving revision 1.2 +diff -u -u -r1.1.1.2 -r1.2 +--- gc/gcconfig.h 1999/06/02 19:29:18 1.1.1.2 ++++ gc/gcconfig.h 1999/06/03 12:44:49 1.2 +@@ -48,6 +48,11 @@ + # define NETBSD + # define mach_type_known + # endif ++# if defined(__NetBSD__) && defined(__powerpc__) ++# define POWERPC ++# define NETBSD ++# define mach_type_known ++# endif + # if defined(vax) + # define VAX + # ifdef ultrix +@@ -486,8 +491,8 @@ + + # ifdef POWERPC + # define MACH_TYPE "POWERPC" +-# define ALIGNMENT 2 + # ifdef MACOS ++# define ALIGNMENT 2 + # ifndef __LOWMEM__ + # include <LowMem.h> + # endif +@@ -497,6 +502,7 @@ + # define DATAEND /* not needed */ + # endif + # ifdef LINUX ++# define ALIGNMENT 2 + # define OS_TYPE "LINUX" + # define HEURISTIC1 + # undef STACK_GRAN +@@ -504,6 +510,14 @@ + # define DATASTART GC_data_start + extern int _end; + # define DATAEND (&_end) ++# endif ++# ifdef NETBSD ++# define ALIGNMENT 4 ++# define OS_TYPE "NETBSD" ++# define HEURISTIC2 ++ extern char etext; ++# define DATASTART GC_data_start ++# define DYNAMIC_LOADING + # endif + # endif + +Index: gc/misc.c +=================================================================== +RCS file: /cvsroot/w3m/gc/misc.c,v +retrieving revision 1.1.1.1 +retrieving revision 1.2 +diff -u -u -r1.1.1.1 -r1.2 +--- gc/misc.c 1999/06/02 19:23:56 1.1.1.1 ++++ gc/misc.c 1999/06/03 12:44:50 1.2 +@@ -433,6 +433,9 @@ + # if defined(LINUX) && defined(SPARC) + GC_init_linuxsparc(); + # endif ++# if defined(NETBSD) && defined(POWERPC) ++ GC_init_netbsd_powerpc(); ++# endif + # ifdef SOLARIS_THREADS + GC_thr_init(); + /* We need dirty bits in order to find live stack sections. */ +Index: gc/os_dep.c +=================================================================== +RCS file: /cvsroot/w3m/gc/os_dep.c,v +retrieving revision 1.1.1.2 +retrieving revision 1.2 +diff -u -u -r1.1.1.2 -r1.2 +--- gc/os_dep.c 1999/06/02 19:29:10 1.1.1.2 ++++ gc/os_dep.c 1999/06/03 12:44:50 1.2 +@@ -84,6 +84,19 @@ + # include <machine/trap.h> + #endif + ++#if defined(NETBSD) && defined(POWERPC) ++ ptr_t GC_data_start; ++ ++ void GC_init_netbsd_powerpc() ++ { ++ extern ptr_t GC_find_limit(); ++ extern char **environ; ++ /* This may need to be environ, without the underscore, for */ ++ /* some versions. */ ++ GC_data_start = GC_find_limit((ptr_t)&environ, FALSE); ++ } ++#endif ++ + #ifdef AMIGA + # include <proto/exec.h> + # include <proto/dos.h> + diff --git a/Patches/mipsel b/Patches/mipsel new file mode 100644 index 0000000..c3c6be1 --- /dev/null +++ b/Patches/mipsel @@ -0,0 +1,184 @@ +diff -ur gc/gcconfig.h w3m/gc/gcconfig.h +--- gc/gcconfig.h Tue Jan 4 14:46:50 2000 ++++ gc/gcconfig.h Fri May 26 00:30:56 2000 +@@ -67,11 +67,13 @@ + # if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) + # define ULTRIX + # else +-# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) +-# define IRIX5 /* or IRIX 6.X */ +-# else +-# define RISCOS /* or IRIX 4.X */ +-# endif ++# if !defined(LINUX) ++# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) ++# define IRIX5 /* or IRIX 6.X */ ++# else ++# define RISCOS /* or IRIX 4.X */ ++# endif ++# endif + # endif + # define mach_type_known + # endif +@@ -836,49 +838,58 @@ + + # ifdef MIPS + # define MACH_TYPE "MIPS" +-# ifndef IRIX5 +-# define DATASTART (ptr_t)0x10000000 ++# ifdef LINUX ++# define OS_TYPE "LINUX" ++ extern int __data_start; ++# define DATASTART ((ptr_t)(&__data_start)) ++# define ALIGNMENT 4 ++# define USE_GENERIC_PUSH_REGS 1 ++# define STACKBOTTOM 0x80000000 ++# else /* LINUX */ ++# ifndef IRIX5 ++# define DATASTART (ptr_t)0x10000000 + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +-# else +- extern int _fdata; +-# define DATASTART ((ptr_t)(&_fdata)) +-# ifdef USE_MMAP +-# define HEAP_START (ptr_t)0x30000000 + # else +-# define HEAP_START DATASTART +-# endif ++ extern int _fdata; ++# define DATASTART ((ptr_t)(&_fdata)) ++# ifdef USE_MMAP ++# define HEAP_START (ptr_t)0x30000000 ++# else ++# define HEAP_START DATASTART ++# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +-# endif /* IRIX5 */ +-# define HEURISTIC2 ++# endif /* IRIX5 */ ++# define HEURISTIC2 + /* # define STACKBOTTOM ((ptr_t)0x7fff8000) sometimes also works. */ +-# ifdef ULTRIX +-# define OS_TYPE "ULTRIX" +-# define ALIGNMENT 4 +-# endif +-# ifdef RISCOS +-# define OS_TYPE "RISCOS" +-# define ALIGNMENT 4 /* Required by hardware */ +-# endif +-# ifdef IRIX5 +-# define OS_TYPE "IRIX5" +-# define MPROTECT_VDB +-# ifdef _MIPS_SZPTR +-# define CPP_WORDSZ _MIPS_SZPTR +-# define ALIGNMENT (_MIPS_SZPTR/8) +-# if CPP_WORDSZ != 64 ++# ifdef ULTRIX ++# define OS_TYPE "ULTRIX" ++# define ALIGNMENT 4 ++# endif ++# ifdef RISCOS ++# define OS_TYPE "RISCOS" ++# define ALIGNMENT 4 /* Required by hardware */ ++# endif ++# ifdef IRIX5 ++# define OS_TYPE "IRIX5" ++# define MPROTECT_VDB ++# ifdef _MIPS_SZPTR ++# define CPP_WORDSZ _MIPS_SZPTR ++# define ALIGNMENT (_MIPS_SZPTR/8) ++# if CPP_WORDSZ != 64 ++# define ALIGN_DOUBLE ++# endif ++# else ++# define ALIGNMENT 4 + # define ALIGN_DOUBLE + # endif +-# else +-# define ALIGNMENT 4 +-# define ALIGN_DOUBLE +-# endif +-# define DYNAMIC_LOADING +-# endif ++# define DYNAMIC_LOADING ++# endif ++# endif + # endif + + # ifdef RS6000 +diff -ur gc/mach_dep.c w3m/gc/mach_dep.c +--- gc/mach_dep.c Tue Jan 4 14:46:50 2000 ++++ gc/mach_dep.c Fri May 26 00:34:11 2000 +@@ -74,6 +74,21 @@ + # ifdef RT + register long TMP_SP; /* must be bound to r11 */ + # endif ++# if defined(MIPS) && defined(LINUX) ++# define call_push(x) asm("move $4," x ";"); asm("jal GC_push_one") ++ call_push("$2"); ++ call_push("$3"); ++ call_push("$16"); ++ call_push("$17"); ++ call_push("$18"); ++ call_push("$19"); ++ call_push("$20"); ++ call_push("$21"); ++ call_push("$22"); ++ call_push("$23"); ++ call_push("$30"); ++# undef call_push ++# endif /* MIPS && LINUX */ + # ifdef VAX + /* VAX - generic code below does not work under 4.2 */ + /* r1 through r5 are caller save, and therefore */ +@@ -338,7 +353,9 @@ + # if !(defined M68K) && !(defined VAX) && !(defined RT) + # if !(defined SPARC) && !(defined I386) && !(defined NS32K) + # if !defined(POWERPC) && !defined(UTS4) ++# if (!defined(MIPS) && !defined(LINUX)) + --> bad news <-- ++# endif + # endif + # endif + # endif +diff -ur gc/misc.c w3m/gc/misc.c +--- gc/misc.c Tue Jan 4 14:46:50 2000 ++++ gc/misc.c Fri May 26 00:52:59 2000 +@@ -433,7 +433,8 @@ + # ifdef MSWIN32 + GC_init_win32(); + # endif +-# if defined(LINUX) && (defined(POWERPC) || defined(ALPHA) || defined(SPARC)) ++# if defined(LINUX) && (defined(POWERPC) || defined(ALPHA) || \ ++ defined(SPARC) || defined(MIPS)) + GC_init_linux_data_start(); + # endif + # ifdef SOLARIS_THREADS +diff -ur gc/os_dep.c w3m/gc/os_dep.c +--- gc/os_dep.c Tue Jan 4 14:46:50 2000 ++++ gc/os_dep.c Fri May 26 00:37:07 2000 +@@ -72,7 +72,8 @@ + # define NEED_FIND_LIMIT + # endif + +-# if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA)) ++# if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || \ ++ defined(ALPHA) || defined(MIPS)) + # define NEED_FIND_LIMIT + # endif + +@@ -139,7 +140,8 @@ + # define OPT_PROT_EXEC 0 + #endif + +-#if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || defined(ALPHA)) ++#if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || \ ++ defined(ALPHA) || defined(MIPS)) + /* The I386 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ diff --git a/Patches/newsos6 b/Patches/newsos6 new file mode 100644 index 0000000..4205511 --- /dev/null +++ b/Patches/newsos6 @@ -0,0 +1,27 @@ +diff -ur gc/Makefile gc/Makefile +--- gc/Makefile Sat Jul 24 02:53:34 1999 ++++ gc/Makefile Tue Nov 30 14:29:50 1999 +@@ -7,7 +7,7 @@ + # and runs some tests of collector and cords. Does not add cords or + # c++ interface to gc.a + # cord/de - builds dumb editor based on cords. +-ABI_FLAG= ++ABI_FLAG=-KPIC + CC=cc $(ABI_FLAG) + CXX=CC $(ABI_FLAG) + AS=as $(ABI_FLAG) +Only in gc: Makefile.back +Only in gc: Makefile.orig +diff -ur gc/gcconfig.h gc/gcconfig.h +--- gc/gcconfig.h Fri Jul 9 05:03:22 1999 ++++ gc/gcconfig.h Tue Nov 30 14:30:11 1999 +@@ -64,7 +64,8 @@ + # endif + # if defined(mips) || defined(__mips) + # define MIPS +-# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) ++# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) ||\ ++ defined(nec_ews) || defined(__sony_news) + # define ULTRIX + # else + # if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) diff --git a/Patches/os2 b/Patches/os2 new file mode 100644 index 0000000..7067d71 --- /dev/null +++ b/Patches/os2 @@ -0,0 +1,39 @@ +--- w3m-0.1.10/XMakefile Thu Jun 8 13:26:04 2000 ++++ w3m-0.1.10-6/XMakefile Sun Jun 11 16:37:18 2000 +@@ -8,7 +8,7 @@ + LOBJS=terms.o conv.o url.o ftp.o anchor.o mimehead.o hash.o tagtable.o + LLOBJS=version.o + ALIBOBJS=Str.o indep.o regex.o textlist.o parsetag.o +-ALIB=libindep.a ++ALIB=indep.a + ALLOBJS=$(OBJS) $(LOBJS) $(LLOBJS) + + TARGET=w3m$(EXT) +@@ -31,13 +31,13 @@ + $(CC) $(CFLAGS) -o $(TARGET) $(ALLOBJS) $(LIBS) + + $(ALIB): $(ALIBOBJS) +- $(AR) rv $(ALIB) $(ALIBOBJS) ++ $(AR) srv $(ALIB) $(ALIBOBJS) + $(RANLIB) $(ALIB) + + $(OBJS): fm.h funcname1.h + + tagtable.c: html.h tagtable.tab mktable$(EXT) +- ./mktable 100 tagtable.tab > tagtable.c ++ mktable 100 tagtable.tab > tagtable.c + + func.o: funcname.c + keybind.o: funcname2.h +--- w3m-0.1.10/gc/os_dep.c Tue Jan 4 14:46:50 2000 ++++ w3m-0.1.10-6/gc/os_dep.c Sun Jun 11 16:37:16 2000 +@@ -732,7 +732,9 @@ + if (!(flags & OBJWRITE)) continue; + if (!(flags & OBJREAD)) continue; + if (flags & OBJINVALID) { ++#ifndef __EMX__ + GC_err_printf0("Object with invalid pages?\n"); ++#endif + continue; + } + GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE); @@ -0,0 +1,3 @@ +If you can read English, see doc/*. +If you can read Japanese, see doc-jp/*. +If you can read both, read both and correct English. :-) @@ -0,0 +1,556 @@ +/* $Id: Str.c,v 1.1 2001/11/08 05:14:08 a-ito Exp $ */ +/* + * String manipulation library for Boehm GC + * + * (C) Copyright 1998-1999 by Akinori Ito + * + * This software may be redistributed freely for this purpose, in full + * or in part, provided that this entire copyright notice is included + * on any copies of this software and applications and derivations thereof. + * + * This software is provided on an "as is" basis, without warranty of any + * kind, either expressed or implied, as to any matter including, but not + * limited to warranty of fitness of purpose, or merchantability, or + * results obtained from use of this software. + */ +#include <stdio.h> +#include <gc.h> +#include <stdarg.h> +#include <string.h> +#ifdef __EMX__ +#include <strings.h> +#endif +#include "Str.h" +#include "myctype.h" + +#define INITIAL_STR_SIZE 32 + +#ifdef STR_DEBUG +/* This is obsolete, because "Str" can handle a '\0' character now. */ +#define STR_LENGTH_CHECK(x) if (((x)->ptr==0&&(x)->length!=0)||(strlen((x)->ptr)!=(x)->length))abort(); +#else /* not STR_DEBUG */ +#define STR_LENGTH_CHECK(x) +#endif /* not STR_DEBUG */ + +Str +Strnew() +{ + Str x = GC_MALLOC(sizeof(struct _Str)); + x->ptr = GC_MALLOC_ATOMIC(INITIAL_STR_SIZE); + x->ptr[0] = '\0'; + x->area_size = INITIAL_STR_SIZE; + x->length = 0; + return x; +} + +Str +Strnew_size(int n) +{ + Str x = GC_MALLOC(sizeof(struct _Str)); + x->ptr = GC_MALLOC_ATOMIC(n + 1); + x->ptr[0] = '\0'; + x->area_size = n + 1; + x->length = 0; + return x; +} + +Str +Strnew_charp(char *p) +{ + Str x; + int n; + + if (p == NULL) + return Strnew(); + x = GC_MALLOC(sizeof(struct _Str)); + n = strlen(p) + 1; + x->ptr = GC_MALLOC_ATOMIC(n); + x->area_size = n; + x->length = n - 1; + bcopy((void *)p, (void*)x->ptr, n); + return x; +} + +Str +Strnew_m_charp(char *p,...) +{ + va_list ap; + Str r = Strnew(); + + va_start(ap, p); + while (p != NULL) { + Strcat_charp(r, p); + p = va_arg(ap, char *); + } + return r; +} + +Str +Strnew_charp_n(char *p, int n) +{ + Str x; + + if (p == NULL) + return Strnew_size(n); + x = GC_MALLOC(sizeof(struct _Str)); + x->ptr = GC_MALLOC_ATOMIC(n + 1); + x->area_size = n + 1; + x->length = n; + bcopy((void *)p, (void *)x->ptr, n); + x->ptr[n] = '\0'; + return x; +} + +Str +Strdup(Str s) +{ + Str n = Strnew_size(s->length); + STR_LENGTH_CHECK(s); + Strcopy(n, s); + return n; +} + +void +Strclear(Str s) +{ + s->length = 0; + s->ptr[0] = '\0'; +} + +void +Strfree(Str x) +{ + GC_free(x->ptr); + GC_free(x); +} + +void +Strcopy(Str x, Str y) +{ + STR_LENGTH_CHECK(x); + STR_LENGTH_CHECK(y); + if (x->area_size < y->length + 1) { + GC_free(x->ptr); + x->ptr = GC_MALLOC_ATOMIC(y->length + 1); + x->area_size = y->length + 1; + } + bcopy((void *)y->ptr, (void *)x->ptr, y->length + 1); + x->length = y->length; +} + +void +Strcopy_charp(Str x, char *y) +{ + int len; + + STR_LENGTH_CHECK(x); + if (y == NULL) { + x->length = 0; + return; + } + len = strlen(y); + if (x->area_size < len + 1) { + GC_free(x->ptr); + x->ptr = GC_MALLOC_ATOMIC(len + 1); + x->area_size = len + 1; + } + bcopy((void *)y, (void *)x->ptr, len + 1); + x->length = len; +} + +void +Strcopy_charp_n(Str x, char *y, int n) +{ + int len = n; + + STR_LENGTH_CHECK(x); + if (y == NULL) { + x->length = 0; + return; + } + if (x->area_size < len + 1) { + GC_free(x->ptr); + x->ptr = GC_MALLOC_ATOMIC(len + 1); + x->area_size = len + 1; + } + bcopy((void *)y, (void *)x->ptr, n); + x->ptr[n] = '\0'; + x->length = n; +} + +void +Strcat_charp_n(Str x, char *y, int n) +{ + int newlen; + + STR_LENGTH_CHECK(x); + if (y == NULL) + return; + newlen = x->length + n + 1; + if (x->area_size < newlen) { + char *old = x->ptr; + newlen = newlen * 3 / 2; + x->ptr = GC_MALLOC_ATOMIC(newlen); + x->area_size = newlen; + bcopy((void *)old, (void *)x->ptr, x->length); + GC_free(old); + } + bcopy((void *)y, (void *)&x->ptr[x->length], n); + x->length += n; + x->ptr[x->length] = '\0'; +} + +void +Strcat(Str x, Str y) +{ + STR_LENGTH_CHECK(y); + Strcat_charp_n(x, y->ptr, y->length); +} + +void +Strcat_charp(Str x, char *y) +{ + if (y == NULL) + return; + Strcat_charp_n(x, y, strlen(y)); +} + +void +Strcat_m_charp(Str x,...) +{ + va_list ap; + char *p; + + va_start(ap, x); + while ((p = va_arg(ap, char *)) != NULL) + Strcat_charp_n(x, p, strlen(p)); +} + +void +Strgrow(Str x) +{ + char *old = x->ptr; + int newlen; + newlen = x->length * 6 / 5; + if (newlen == x->length) + newlen += 2; + x->ptr = GC_MALLOC_ATOMIC(newlen); + x->area_size = newlen; + bcopy((void *)old, (void *)x->ptr, x->length); + GC_free(old); +} + +Str +Strsubstr(Str s, int beg, int len) +{ + Str new_s; + int i; + + STR_LENGTH_CHECK(s); + new_s = Strnew(); + if (beg >= s->length) + return new_s; + for (i = 0; i < len && beg + i < s->length; i++) + Strcat_char(new_s, s->ptr[beg + i]); + return new_s; +} + +void +Strlower(Str s) +{ + int i; + STR_LENGTH_CHECK(s); + for (i = 0; i < s->length; i++) + s->ptr[i] = tolower(s->ptr[i]); +} + +void +Strupper(Str s) +{ + int i; + STR_LENGTH_CHECK(s); + for (i = 0; i < s->length; i++) + s->ptr[i] = toupper(s->ptr[i]); +} + +void +Strchop(Str s) +{ + STR_LENGTH_CHECK(s); + while ((s->ptr[s->length - 1] == '\n' || s->ptr[s->length - 1] == '\r') && + s->length > 0) { + s->length--; + } + s->ptr[s->length] = '\0'; +} + +void +Strinsert_char(Str s, int pos, char c) +{ + int i; + STR_LENGTH_CHECK(s); + if (pos < 0 || s->length < pos) + return; + if (s->length + 2 > s->area_size) + Strgrow(s); + for (i = s->length; i > pos; i--) + s->ptr[i] = s->ptr[i - 1]; + s->ptr[++s->length] = '\0'; + s->ptr[pos] = c; +} + +void +Strinsert_charp(Str s, int pos, char *p) +{ + STR_LENGTH_CHECK(s); + while (*p) + Strinsert_char(s, pos++, *(p++)); +} + +void +Strdelete(Str s, int pos, int n) +{ + int i; + STR_LENGTH_CHECK(s); + if (s->length <= pos + n) { + s->ptr[pos] = '\0'; + s->length = pos; + return; + } + for (i = pos; i < s->length - n; i++) + s->ptr[i] = s->ptr[i + n]; + s->ptr[i] = '\0'; + s->length = i; +} + +void +Strtruncate(Str s, int pos) +{ + STR_LENGTH_CHECK(s); + s->ptr[pos] = '\0'; + s->length = pos; +} + +void +Strshrink(Str s, int n) +{ + STR_LENGTH_CHECK(s); + if (n >= s->length) { + s->length = 0; + s->ptr[0] = '\0'; + } + else { + s->length -= n; + s->ptr[s->length] = '\0'; + } +} + +void +Strremovefirstspaces(Str s) +{ + int i; + + STR_LENGTH_CHECK(s); + for (i = 0; i < s->length && IS_SPACE(s->ptr[i]); i++); + if (i == 0) + return; + Strdelete(s, 0, i); +} + +void +Strremovetrailingspaces(Str s) +{ + int i; + + STR_LENGTH_CHECK(s); + for (i = s->length - 1; i >= 0 && IS_SPACE(s->ptr[i]); i--); + s->length = i + 1; + s->ptr[i + 1] = '\0'; +} + +Str +Stralign_left(Str s, int width) +{ + Str n; + int i; + + STR_LENGTH_CHECK(s); + if (s->length >= width) + return Strdup(s); + n = Strnew_size(width); + Strcopy(n, s); + for (i = s->length; i < width; i++) + Strcat_char(n, ' '); + return n; +} + +Str +Stralign_right(Str s, int width) +{ + Str n; + int i; + + STR_LENGTH_CHECK(s); + if (s->length >= width) + return Strdup(s); + n = Strnew_size(width); + for (i = s->length; i < width; i++) + Strcat_char(n, ' '); + Strcat(n, s); + return n; +} + +Str +Stralign_center(Str s, int width) +{ + Str n; + int i, w; + + STR_LENGTH_CHECK(s); + if (s->length >= width) + return Strdup(s); + n = Strnew_size(width); + w = (width - s->length) / 2; + for (i = 0; i < w; i++) + Strcat_char(n, ' '); + Strcat(n, s); + for (i = w + s->length; i < width; i++) + Strcat_char(n, ' '); + return n; +} + +#define SP_NORMAL 0 +#define SP_PREC 1 +#define SP_PREC2 2 + +Str +Sprintf(char *fmt,...) +{ + int len = 0; + int status = SP_NORMAL; + int p; + char *f; + Str s; + va_list ap; + + va_start(ap, fmt); + for (f = fmt; *f; f++) { + redo: + switch (status) { + case SP_NORMAL: + if (*f == '%') { + status = SP_PREC; + p = 0; + } + else + len++; + break; + case SP_PREC: + if (IS_ALPHA(*f)) { + /* conversion char. */ + double vd; + int vi; + char *vs; + void *vp; + + switch (*f) { + case 'l': + case 'h': + case 'L': + case 'w': + continue; + case 'd': + case 'i': + case 'o': + case 'x': + case 'X': + case 'u': + vi = va_arg(ap, int); + len += (p > 0) ? p : 10; + break; + case 'f': + case 'g': + case 'e': + case 'G': + case 'E': + vd = va_arg(ap, double); + len += (p > 0) ? p : 15; + break; + case 'c': + len += 1; + vi = va_arg(ap, int); + break; + case 's': + vs = va_arg(ap, char *); + vi = strlen(vs); + len += (p > vi) ? p : vi; + break; + case 'p': + vp = va_arg(ap, void *); + len += 10; + break; + case 'n': + vp = va_arg(ap, void *); + break; + } + status = SP_NORMAL; + } + else if (IS_DIGIT(*f)) + p = p * 10 + *f - '0'; + else if (*f == '.') + status = SP_PREC2; + else if (*f == '%') { + status = SP_NORMAL; + len++; + } + break; + case SP_PREC2: + if (IS_ALPHA(*f)) { + status = SP_PREC; + goto redo; + } + break; + } + } + va_end(ap); + s = Strnew_size(len * 2); + va_start(ap, fmt); + vsprintf(s->ptr, fmt, ap); + va_end(ap); + s->length = strlen(s->ptr); + if (s->length > len * 2) { + fprintf(stderr, "Sprintf: string too long\n"); + exit(1); + } + return s; +} + +Str +Strfgets(FILE * f) +{ + Str s = Strnew(); + char c; + while (1) { + c = fgetc(f); + if (feof(f) || ferror(f)) + break; + Strcat_char(s, c); + if (c == '\n') + break; + } + return s; +} + +Str +Strfgetall(FILE * f) +{ + Str s = Strnew(); + char c; + while (1) { + c = fgetc(f); + if (feof(f) || ferror(f)) + break; + Strcat_char(s, c); + } + return s; +} @@ -0,0 +1,83 @@ +/* $Id: Str.h,v 1.1 2001/11/08 05:14:10 a-ito Exp $ */ +/* + * String manipulation library for Boehm GC + * + * (C) Copyright 1998-1999 by Akinori Ito + * + * This software may be redistributed freely for this purpose, in full + * or in part, provided that this entire copyright notice is included + * on any copies of this software and applications and derivations thereof. + * + * This software is provided on an "as is" basis, without warranty of any + * kind, either expressed or implied, as to any matter including, but not + * limited to warranty of fitness of purpose, or merchantability, or + * results obtained from use of this software. + */ +#ifndef GC_STR_H +#define GC_STR_H +#include <stdio.h> +#include <string.h> +#ifdef __EMX__ +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +typedef struct _Str { + char *ptr; + int length; + int area_size; +} *Str; + +Str Strnew(); +Str Strnew_size(int); +Str Strnew_charp(char *); +Str Strnew_charp_n(char *, int); +Str Strnew_m_charp(char *,...); +Str Strdup(Str); +void Strclear(Str); +void Strfree(Str); +void Strcopy(Str, Str); +void Strcopy_charp(Str, char *); +void Strcopy_charp_n(Str, char *, int); +void Strcat_charp_n(Str, char *, int); +void Strcat(Str, Str); +void Strcat_charp(Str, char *); +void Strcat_m_charp(Str,...); +Str Strsubstr(Str, int, int); +void Strinsert_char(Str, int, char); +void Strinsert_charp(Str, int, char *); +void Strdelete(Str, int, int); +void Strtruncate(Str, int); +void Strlower(Str); +void Strupper(Str); +void Strchop(Str); +void Strshrink(Str, int); +void Strshrinkfirst(Str, int); +void Strremovefirstspaces(Str); +void Strremovetrailingspaces(Str); +Str Stralign_left(Str, int); +Str Stralign_right(Str, int); +Str Stralign_center(Str, int); + +Str Sprintf(char *fmt,...); + +Str Strfgets(FILE *); +Str Strfgetall(FILE *); + +void Strgrow(Str s); + +#define Strcat_char(x,y) (((x)->length+1>=(x)->area_size)?Strgrow(x),0:0,(x)->ptr[(x)->length++]=(y),(x)->ptr[(x)->length]=0) +#define Strcmp(x,y) strcmp((x)->ptr,(y)->ptr) +#define Strcmp_charp(x,y) strcmp((x)->ptr,(y)) +#define Strncmp(x,y,n) strncmp((x)->ptr,(y)->ptr,(n)) +#define Strncmp_charp(x,y,n) strncmp((x)->ptr,y,(n)) +#define Strcasecmp(x,y) strcasecmp((x)->ptr,(y)->ptr) +#define Strcasecmp_charp(x,y) strcasecmp((x)->ptr,(y)) +#define Strncasecmp(x,y,n) strncasecmp((x)->ptr,(y)->ptr,(n)) +#define Strncasecmp_charp(x,y,n) strncasecmp((x)->ptr,y,(n)) + +#define Strlastchar(s) ((s)->length>0?(s)->ptr[(s)->length-1]:'\0') +#define Strinsert(s,n,p) Strinsert_charp(s,n,(p)->ptr) +#define Strshrinkfirst(s,n) Strdelete(s,0,n) +#define Strfputs(s,f) fwrite((s)->ptr,1,(s)->length,(f)) +#endif /* not GC_STR_H */ diff --git a/XMakefile b/XMakefile new file mode 100644 index 0000000..23e8b11 --- /dev/null +++ b/XMakefile @@ -0,0 +1,106 @@ +SRCS=main.c file.c buffer.c display.c etc.c search.c linein.c table.c local.c \ + form.c map.c frame.c rc.c menu.c mailcap.c\ + func.c cookie.c history.c backend.c $(KEYBIND_SRC) +OBJS=main.o file.o buffer.o display.o etc.o search.o linein.o table.o local.o\ + form.o map.o frame.o rc.o menu.o mailcap.o\ + func.o cookie.o history.o backend.o $(KEYBIND_OBJ) +LSRCS=terms.c conv.c url.c ftp.c anchor.c mimehead.c hash.c parsetagx.c\ + tagtable.c istream.c +LOBJS=terms.o conv.o url.o ftp.o anchor.o mimehead.o hash.o parsetagx.o\ + tagtable.o istream.o +LLOBJS=version.o +ALIBOBJS=Str.o indep.o regex.o textlist.o parsetag.o myctype.o +ALIB=libindep.a +ALLOBJS=$(OBJS) $(LOBJS) $(LLOBJS) + +TARGET=w3m$(EXT) +BOOKMARKER=w3mbookmark$(EXT) +HELPER=w3mhelperpanel$(EXT) +TARGETS=$(TARGET) $(BOOKMARKER) $(HELPER) + +INCLUDES=-I. + +DEFS=$(INCLUDES) # -DDEBUG +CFLAGS=$(MYCFLAGS) $(DEFS) +LIBS=-L. -lindep $(GCLIB) $(MATHLIB) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) +INSTALL=sh install.sh +INSTALL2=sh ../install.sh +AR=ar + +all: $(TARGETS) + +$(TARGET): $(ALLOBJS) $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(TARGET) $(ALLOBJS) $(LIBS) + +$(ALIB): $(ALIBOBJS) + $(AR) rv $(ALIB) $(ALIBOBJS) + $(RANLIB) $(ALIB) + +$(OBJS): fm.h funcname1.h + +tagtable.c: html.h tagtable.tab mktable$(EXT) + ./mktable 100 tagtable.tab > tagtable.c + +func.o: funcname.c +keybind.o: funcname2.h +keybind_lynx.o: funcname2.h +parsetagx.o: html.c + +funcname.c: funcname.tab + awk -f funcname0.awk funcname.tab > funcname.c + +funcname1.h: funcname.tab + awk -f funcname1.awk funcname.tab > funcname1.h + +funcname2.h: funcname.tab + awk -f funcname2.awk funcname.tab > funcname2.h + +mktable$(EXT): mktable.o hash.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o mktable$(EXT) mktable.o hash.o $(LIBS) + +$(BOOKMARKER): w3mbookmark.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(BOOKMARKER) w3mbookmark.o $(LIBS) + +$(HELPER): w3mhelperpanel.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(HELPER) w3mhelperpanel.o $(LIBS) + +gc/gc.a: + cd gc; make CC='$(CC)' CFLAGS='$(GCCFLAGS)' + +install: $(TARGETS) + -$(MKDIR) $(DESTDIR)$(BIN_DIR) + -$(MKDIR) $(DESTDIR)$(HELP_DIR) + -$(MKDIR) $(DESTDIR)$(LIB_DIR) + $(INSTALL) -m 755 $(TARGET) $(DESTDIR)$(BIN_DIR)/$(TARGET) + $(INSTALL) -m 644 w3mhelp-w3m_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_en.html + $(INSTALL) -m 644 w3mhelp-w3m_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_ja.html + $(INSTALL) -m 644 w3mhelp-lynx_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_en.html + $(INSTALL) -m 644 w3mhelp-lynx_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_ja.html + $(INSTALL) -m 644 $(HELP_FILE) $(DESTDIR)$(HELP_DIR)/w3mhelp.html + for d in $(BOOKMARKER) $(HELPER); do $(INSTALL) -m 755 $$d $(DESTDIR)$(LIB_DIR)/$$d; done + (cd scripts; for i in *.cgi; do $(INSTALL2) -m 755 $$i $(DESTDIR)$(LIB_DIR)/$$i; done) + +uninstall: + -$(RM) $(BIN_DIR)/$(TARGET) + -$(RM) $(HELP_DIR)/w3mhelp-lynx_en.html + -$(RM) $(HELP_DIR)/w3mhelp-lynx_ja.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_en.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_ja.html + -$(RM) $(HELP_DIR)/w3mhelp.html + +clean: sweep + rm -f *.o *.a $(TARGETS) mktable$(EXT) + +sweep: + -rm -f core *~ *.bak *.orig *.rej + +depend: + makedepend $(CFLAGS) *.c + +dist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION).tar.gz w3m-$(VERSION); mv w3m-$(VERSION) w3m + +bindist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION)-$(MODEL).tar.gz w3m-$(VERSION)/{w3m*,doc*,Bonus*,README,scripts}; mv w3m-$(VERSION) w3m + +# DO NOT DELETE diff --git a/XMakefile.dist b/XMakefile.dist new file mode 100644 index 0000000..6d907c7 --- /dev/null +++ b/XMakefile.dist @@ -0,0 +1,106 @@ +SRCS=main.c file.c buffer.c display.c etc.c search.c linein.c table.c local.c \ + form.c map.c frame.c rc.c menu.c mailcap.c\ + func.c cookie.c history.c $(KEYBIND_SRC) +OBJS=main.o file.o buffer.o display.o etc.o search.o linein.o table.o local.o\ + form.o map.o frame.o rc.o menu.o mailcap.o\ + func.o cookie.o history.o $(KEYBIND_OBJ) +LSRCS=terms.c conv.c url.c ftp.c anchor.c mimehead.c hash.c parsetagx.c\ + tagtable.c istream.c +LOBJS=terms.o conv.o url.o ftp.o anchor.o mimehead.o hash.o parsetagx.o\ + tagtable.o istream.o +LLOBJS=version.o +ALIBOBJS=Str.o indep.o regex.o textlist.o parsetag.o myctype.o +ALIB=libindep.a +ALLOBJS=$(OBJS) $(LOBJS) $(LLOBJS) + +TARGET=w3m$(EXT) +BOOKMARKER=w3mbookmark$(EXT) +HELPER=w3mhelperpanel$(EXT) +TARGETS=$(TARGET) $(BOOKMARKER) $(HELPER) + +INCLUDES=-I. + +DEFS=$(INCLUDES) # -DDEBUG +CFLAGS=$(MYCFLAGS) $(DEFS) +LIBS=-L. -lindep $(GCLIB) $(MATHLIB) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) +INSTALL=sh install.sh +INSTALL2=sh ../install.sh +AR=ar + +all: $(TARGETS) + +$(TARGET): $(ALLOBJS) $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(TARGET) $(ALLOBJS) $(LIBS) + +$(ALIB): $(ALIBOBJS) + $(AR) rv $(ALIB) $(ALIBOBJS) + $(RANLIB) $(ALIB) + +$(OBJS): fm.h funcname1.h + +tagtable.c: html.h tagtable.tab mktable$(EXT) + ./mktable 100 tagtable.tab > tagtable.c + +func.o: funcname.c +keybind.o: funcname2.h +keybind_lynx.o: funcname2.h +parsetagx.o: html.c + +funcname.c: funcname.tab + awk -f funcname0.awk funcname.tab > funcname.c + +funcname1.h: funcname.tab + awk -f funcname1.awk funcname.tab > funcname1.h + +funcname2.h: funcname.tab + awk -f funcname2.awk funcname.tab > funcname2.h + +mktable$(EXT): mktable.o hash.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o mktable$(EXT) mktable.o hash.o $(LIBS) + +$(BOOKMARKER): w3mbookmark.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(BOOKMARKER) w3mbookmark.o $(LIBS) + +$(HELPER): w3mhelperpanel.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(HELPER) w3mhelperpanel.o $(LIBS) + +gc/gc.a: + cd gc; make CC='$(CC)' CFLAGS='$(GCCFLAGS)' + +install: $(TARGETS) + -$(MKDIR) $(DESTDIR)$(BIN_DIR) + -$(MKDIR) $(DESTDIR)$(HELP_DIR) + -$(MKDIR) $(DESTDIR)$(LIB_DIR) + $(INSTALL) -m 755 $(TARGET) $(DESTDIR)$(BIN_DIR)/$(TARGET) + $(INSTALL) -m 644 w3mhelp-w3m_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_en.html + $(INSTALL) -m 644 w3mhelp-w3m_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_ja.html + $(INSTALL) -m 644 w3mhelp-lynx_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_en.html + $(INSTALL) -m 644 w3mhelp-lynx_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_ja.html + $(INSTALL) -m 644 $(HELP_FILE) $(DESTDIR)$(HELP_DIR)/w3mhelp.html + for d in $(BOOKMARKER) $(HELPER); do $(INSTALL) -m 755 $$d $(DESTDIR)$(LIB_DIR)/$$d; done + (cd scripts; for i in *.cgi; do $(INSTALL2) -m 755 $$i $(DESTDIR)$(LIB_DIR)/$$i; done) + +uninstall: + -$(RM) $(BIN_DIR)/$(TARGET) + -$(RM) $(HELP_DIR)/w3mhelp-lynx_en.html + -$(RM) $(HELP_DIR)/w3mhelp-lynx_ja.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_en.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_ja.html + -$(RM) $(HELP_DIR)/w3mhelp.html + +clean: sweep + rm -f *.o *.a $(TARGETS) mktable$(EXT) + +sweep: + -rm -f core *~ *.bak *.orig *.rej + +depend: + makedepend $(CFLAGS) *.c + +dist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION).tar.gz w3m-$(VERSION); mv w3m-$(VERSION) w3m + +bindist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION)-$(MODEL).tar.gz w3m-$(VERSION)/{w3m*,doc*,Bonus*,README,scripts}; mv w3m-$(VERSION) w3m + +# DO NOT DELETE diff --git a/XXMakefile b/XXMakefile new file mode 100644 index 0000000..4ebb0c6 --- /dev/null +++ b/XXMakefile @@ -0,0 +1,128 @@ +#ifdef makefile_parameter + +BIN_DIR = /usr/local/bin +HELP_DIR = /usr/local/lib/w3m +LIB_DIR = /usr/local/lib/w3m +HELP_FILE = w3mhelp-w3m_ja.html +SYS_LIBRARIES = -lgpm -lbsd -lnsl -ltermcap -L/usr/local/ssl/lib -lssl -lcrypto +LOCAL_LIBRARIES = +CC = gcc +MYCFLAGS = -g -Wall -I./gc/include -I/usr/local/ssl/include/openssl -I/usr/local/ssl/include +GCCFLAGS = -g -Wall -I./gc/include -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DSILENT -DNO_DEBUGGING #-DNO_SIGNALS +KEYBIND_SRC = keybind.c +KEYBIND_OBJ = keybind.o +EXT= +MATHLIB=-lm +GCLIB=gc/gc.a +GCTARGET=gc/gc.a +RANLIB=ranlib +MKDIR=mkdir -p +VERSION=0.2.1 +MODEL=Linux.i686-monster-ja +#else +SRCS=main.c file.c buffer.c display.c etc.c search.c linein.c table.c local.c \ + form.c map.c frame.c rc.c menu.c mailcap.c\ + func.c cookie.c history.c backend.c $(KEYBIND_SRC) +OBJS=main.o file.o buffer.o display.o etc.o search.o linein.o table.o local.o\ + form.o map.o frame.o rc.o menu.o mailcap.o\ + func.o cookie.o history.o backend.o $(KEYBIND_OBJ) +LSRCS=terms.c conv.c url.c ftp.c anchor.c mimehead.c hash.c parsetagx.c\ + tagtable.c istream.c +LOBJS=terms.o conv.o url.o ftp.o anchor.o mimehead.o hash.o parsetagx.o\ + tagtable.o istream.o +LLOBJS=version.o +ALIBOBJS=Str.o indep.o regex.o textlist.o parsetag.o myctype.o +ALIB=libindep.a +ALLOBJS=$(OBJS) $(LOBJS) $(LLOBJS) + +TARGET=w3m$(EXT) +BOOKMARKER=w3mbookmark$(EXT) +HELPER=w3mhelperpanel$(EXT) +TARGETS=$(TARGET) $(BOOKMARKER) $(HELPER) + +INCLUDES=-I. + +DEFS=$(INCLUDES) # -DDEBUG +CFLAGS=$(MYCFLAGS) $(DEFS) +LIBS=-L. -lindep $(GCLIB) $(MATHLIB) $(LOCAL_LIBRARIES) $(SYS_LIBRARIES) +INSTALL=sh install.sh +INSTALL2=sh ../install.sh +AR=ar + +all: $(TARGETS) + +$(TARGET): $(ALLOBJS) $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(TARGET) $(ALLOBJS) $(LIBS) + +$(ALIB): $(ALIBOBJS) + $(AR) rv $(ALIB) $(ALIBOBJS) + $(RANLIB) $(ALIB) + +$(OBJS): fm.h funcname1.h + +tagtable.c: html.h tagtable.tab mktable$(EXT) + ./mktable 100 tagtable.tab > tagtable.c + +func.o: funcname.c +keybind.o: funcname2.h +keybind_lynx.o: funcname2.h +parsetagx.o: html.c + +funcname.c: funcname.tab + awk -f funcname0.awk funcname.tab > funcname.c + +funcname1.h: funcname.tab + awk -f funcname1.awk funcname.tab > funcname1.h + +funcname2.h: funcname.tab + awk -f funcname2.awk funcname.tab > funcname2.h + +mktable$(EXT): mktable.o hash.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o mktable$(EXT) mktable.o hash.o $(LIBS) + +$(BOOKMARKER): w3mbookmark.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(BOOKMARKER) w3mbookmark.o $(LIBS) + +$(HELPER): w3mhelperpanel.o $(ALIB) $(GCTARGET) + $(CC) $(CFLAGS) -o $(HELPER) w3mhelperpanel.o $(LIBS) + +gc/gc.a: + cd gc; make CC='$(CC)' CFLAGS='$(GCCFLAGS)' + +install: $(TARGETS) + -$(MKDIR) $(DESTDIR)$(BIN_DIR) + -$(MKDIR) $(DESTDIR)$(HELP_DIR) + -$(MKDIR) $(DESTDIR)$(LIB_DIR) + $(INSTALL) -m 755 $(TARGET) $(DESTDIR)$(BIN_DIR)/$(TARGET) + $(INSTALL) -m 644 w3mhelp-w3m_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_en.html + $(INSTALL) -m 644 w3mhelp-w3m_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-w3m_ja.html + $(INSTALL) -m 644 w3mhelp-lynx_en.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_en.html + $(INSTALL) -m 644 w3mhelp-lynx_ja.html $(DESTDIR)$(HELP_DIR)/w3mhelp-lynx_ja.html + $(INSTALL) -m 644 $(HELP_FILE) $(DESTDIR)$(HELP_DIR)/w3mhelp.html + for d in $(BOOKMARKER) $(HELPER); do $(INSTALL) -m 755 $$d $(DESTDIR)$(LIB_DIR)/$$d; done + (cd scripts; for i in *.cgi; do $(INSTALL2) -m 755 $$i $(DESTDIR)$(LIB_DIR)/$$i; done) + +uninstall: + -$(RM) $(BIN_DIR)/$(TARGET) + -$(RM) $(HELP_DIR)/w3mhelp-lynx_en.html + -$(RM) $(HELP_DIR)/w3mhelp-lynx_ja.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_en.html + -$(RM) $(HELP_DIR)/w3mhelp-w3m_ja.html + -$(RM) $(HELP_DIR)/w3mhelp.html + +clean: sweep + rm -f *.o *.a $(TARGETS) mktable$(EXT) + +sweep: + -rm -f core *~ *.bak *.orig *.rej + +depend: + makedepend $(CFLAGS) *.c + +dist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION).tar.gz w3m-$(VERSION); mv w3m-$(VERSION) w3m + +bindist: + cd ..; mv w3m w3m-$(VERSION); tar cvfz w3m-$(VERSION)-$(MODEL).tar.gz w3m-$(VERSION)/{w3m*,doc*,Bonus*,README,scripts}; mv w3m-$(VERSION) w3m + +# DO NOT DELETE diff --git a/anchor.c b/anchor.c new file mode 100644 index 0000000..6005899 --- /dev/null +++ b/anchor.c @@ -0,0 +1,420 @@ +/* $Id: anchor.c,v 1.1 2001/11/08 05:14:10 a-ito Exp $ */ +#ifdef __EMX__ +#include <strings.h> +#endif + +#include "fm.h" +#include "myctype.h" +#include "regex.h" + +#define FIRST_ANCHOR_SIZE 30 + +AnchorList * +putAnchor(AnchorList * al, char *url, char *target, Anchor ** anchor_return, char *referer, int line, int pos) +{ + int n, i, j; + Anchor *a; + BufferPoint bp; + if (al == NULL) { + al = New(AnchorList); + al->anchors = NULL; + al->nanchor = al->anchormax = 0; + al->acache = -1; + } + if (al->anchormax == 0) { + /* first time; allocate anchor buffer */ + al->anchors = New_N(Anchor, FIRST_ANCHOR_SIZE); + al->anchormax = FIRST_ANCHOR_SIZE; + } + if (al->nanchor == al->anchormax) { /* need realloc */ + al->anchormax *= 2; + al->anchors = New_Reuse(Anchor, al->anchors, + al->anchormax); + } + bp.line = line; + bp.pos = pos; + n = al->nanchor; + if (!n || bpcmp(al->anchors[n-1].start, bp) < 0) + i = n; + else + for (i = 0; i < n; i++) { + if (bpcmp(al->anchors[i].start, bp) >= 0) { + for (j = n; j > i; j--) + al->anchors[j] = al->anchors[j - 1]; + break; + } + } + a = &al->anchors[i]; + a->url = url; + a->target = target; + a->referer = referer; + a->start = bp; + a->end = bp; + al->nanchor++; + if (anchor_return) + *anchor_return = a; + return al; +} + + +Anchor * +registerHref(Buffer * buf, char *url, char *target, char *referer, int line, int pos) +{ + Anchor *a; + buf->href = putAnchor(buf->href, url, target, &a, referer, line, pos); + return a; +} + +Anchor * +registerName(Buffer * buf, char *url, int line, int pos) +{ + Anchor *a; + buf->name = putAnchor(buf->name, url, NULL, &a, NULL, line, pos); + return a; +} + +Anchor * +registerImg(Buffer * buf, char *url, int line, int pos) +{ + Anchor *a; + buf->img = putAnchor(buf->img, url, NULL, &a, NULL, line, pos); + return a; +} + +Anchor * +registerForm(Buffer * buf, FormList * flist, struct parsed_tag * tag, int line, int pos) +{ + Anchor *a; + FormItemList *fi; + + fi = formList_addInput(flist, tag); + if (fi == NULL) + return NULL; + buf->formitem = putAnchor(buf->formitem, + (char *) fi, + flist->target, + &a, + NULL, + line, pos); + fi->anchor_num = buf->formitem->nanchor - 1; + return a; +} + +int +onAnchor(Anchor * a, int line, int pos) +{ + BufferPoint bp; + bp.line = line; + bp.pos = pos; + + if (bpcmp(bp, a->start) < 0) + return -1; + if (bpcmp(a->end, bp) <= 0) + return 1; + return 0; +} + +Anchor * +retrieveAnchor(AnchorList * al, int line, int pos) +{ + Anchor *a; + size_t b, e; + int cmp; + + if (al == NULL || al->nanchor == 0) + return NULL; + + if (al->acache < 0 || al->acache >= al->nanchor) + al->acache = 0; + + for (b = 0, e = al->nanchor - 1; b <= e; al->acache = (b + e) / 2) { + a = &al->anchors[al->acache]; + cmp = onAnchor(a, line, pos); + if (cmp == 0) + return a; + else if (cmp > 0) + b = al->acache + 1; + else if (al->acache == 0) + return NULL; + else + e = al->acache - 1; + } + return NULL; +} + +Anchor * +retrieveCurrentAnchor(Buffer * buf) +{ + if (buf->currentLine == NULL) + return NULL; + return retrieveAnchor(buf->href, + buf->currentLine->linenumber, + buf->pos); +} + +Anchor * +retrieveCurrentImg(Buffer * buf) +{ + if (buf->currentLine == NULL) + return NULL; + return retrieveAnchor(buf->img, + buf->currentLine->linenumber, + buf->pos); +} + +Anchor * +retrieveCurrentForm(Buffer * buf) +{ + if (buf->currentLine == NULL) + return NULL; + return retrieveAnchor(buf->formitem, + buf->currentLine->linenumber, + buf->pos); +} + +Anchor * +searchAnchor(AnchorList * al, char *str) +{ + int i; + Anchor *a; + if (al == NULL) + return NULL; + for (i = 0; i < al->nanchor; i++) { + a = &al->anchors[i]; + if (!strcmp(a->url, str)) + return a; + } + return NULL; +} + +Anchor * +searchURLLabel(Buffer * buf, char *url) +{ + return searchAnchor(buf->name, url); +} + +#ifdef USE_NNTP +static Anchor * +_put_anchor_news(Buffer * buf, char *p1, char *p2, int line, int pos) +{ + Str tmp = Strnew_charp("news:"); + + p1++; + if (*(p2 - 1) == '>') + p2--; + while (p1 < p2) { + Strcat_char(tmp, *(p1++)); + } + return registerHref(buf, tmp->ptr, NULL, NO_REFERER, line, pos); +} +#endif /* USE_NNTP */ + +static Anchor * +_put_anchor_all(Buffer * buf, char *p1, char *p2, int line, int pos) +{ + return registerHref(buf, allocStr(p1, p2 - p1), NULL, NO_REFERER, line, pos); +} + +static void +reseq_anchor0(AnchorList * al, short *seqmap) +{ + int i; + Anchor *a; + + if (!al) + return; + + for (i = 0; i < al->nanchor; i++) { + a = &al->anchors[i]; + if (a->hseq >= 0) { + a->hseq = seqmap[a->hseq]; + } + } +} + +/* renumber anchor */ +static void +reseq_anchor(Buffer * buf) +{ + int i, j, n, nmark = (buf->hmarklist) ? buf->hmarklist->nmark : 0; + short *seqmap; + Anchor *a, *a1; + HmarkerList *ml = NULL; + + if (!buf->href) + return; + + n = nmark; + for (i = 0; i < buf->href->nanchor; i++) { + a = &buf->href->anchors[i]; + if (a->hseq == -2) + n++; + } + + if (n == nmark) + return; + + seqmap = NewAtom_N(short, n); + + for (i = 0; i < n; i++) + seqmap[i] = i; + + n = nmark; + for (i = 0; i < buf->href->nanchor; i++) { + a = &buf->href->anchors[i]; + if (a->hseq == -2) { + a->hseq = n; + a1 = closest_next_anchor(buf->href, NULL, a->start.pos, a->start.line); + a1 = closest_next_anchor(buf->formitem, a1, a->start.pos, a->start.line); + if (a1 && a1->hseq >= 0) { + seqmap[n] = seqmap[a1->hseq]; + for (j = a1->hseq; j < nmark; j++) + seqmap[j]++; + } + ml = putHmarker(ml, a->start.line, a->start.pos, seqmap[n]); + n++; + } + } + + for (i = 0; i < nmark; i++) { + ml = putHmarker(ml, buf->hmarklist->marks[i].line, + buf->hmarklist->marks[i].pos, seqmap[i]); + } + buf->hmarklist = ml; + + reseq_anchor0(buf->href, seqmap); + reseq_anchor0(buf->formitem, seqmap); +} + +/* search regexp and register them as anchors */ +/* returns error message if any */ +static char * +reAnchorAny(Buffer * buf, char *re, Anchor * (*anchorproc) (Buffer *, char *, char *, int, int)) +{ + Line *l; + char *p, *p1, *p2; + Anchor *a; + int i; + int spos, epos; + + if (re == NULL || *re == '\0') { + return NULL; + } + if ((re = regexCompile(re, 1)) != NULL) { + return re; + } + for (l = buf->firstLine; l != NULL; l = l->next) { + p = l->lineBuf; + for (;;) { + if (regexMatch(p, &l->lineBuf[l->len] - p, p == l->lineBuf) == 1) { + matchedPosition(&p1, &p2); + spos = p1 - l->lineBuf; + epos = p2 - l->lineBuf; + for (i = spos; i < epos; i++) { + if (l->propBuf[i] & (PE_ANCHOR | PE_FORM)) + goto _next; + } + a = anchorproc(buf, p1, p2, l->linenumber, p1 - l->lineBuf); + a->end.line = l->linenumber; + a->end.pos = epos; + a->hseq = -2; + for (i = a->start.pos; i < a->end.pos; i++) + l->propBuf[i] |= PE_ANCHOR; + _next: + p = p2; + } + else + break; + } + } + reseq_anchor(buf); + return NULL; +} + +char * +reAnchor(Buffer * buf, char *re) +{ + return reAnchorAny(buf, re, _put_anchor_all); +} + +#ifdef USE_NNTP +char * +reAnchorNews(Buffer * buf, char *re) +{ + return reAnchorAny(buf, re, _put_anchor_news); +} +#endif /* USE_NNTP */ + +#define FIRST_MARKER_SIZE 30 +HmarkerList * +putHmarker(HmarkerList * ml, int line, int pos, int seq) +{ + if (ml == NULL) { + ml = New(HmarkerList); + ml->marks = NULL; + ml->nmark = 0; + ml->markmax = 0; + ml->prevhseq = -1; + } + if (ml->markmax == 0) { + ml->markmax = FIRST_MARKER_SIZE; + ml->marks = New_N(BufferPoint, ml->markmax); +#ifdef __CYGWIN__ + bzero((char *) ml->marks, sizeof(BufferPoint) * ml->markmax); +#else /* not __CYGWIN__ */ + bzero(ml->marks, sizeof(BufferPoint) * ml->markmax); +#endif /* not __CYGWIN__ */ + } + if (seq + 1 > ml->nmark) + ml->nmark = seq + 1; + if (ml->nmark >= ml->markmax) { + ml->markmax = ml->nmark * 2; + ml->marks = New_Reuse(BufferPoint, ml->marks, + ml->markmax); + } + ml->marks[seq].line = line; + ml->marks[seq].pos = pos; + return ml; +} + +Anchor * +closest_next_anchor(AnchorList * a, Anchor * an, int x, int y) +{ + int i; + + if (a == NULL || a->nanchor == 0) + return an; + for (i = 0; i < a->nanchor; i++) { + if (a->anchors[i].hseq < 0) + continue; + if (a->anchors[i].start.line > y || + (a->anchors[i].start.line == y && a->anchors[i].start.pos > x)) { + if (an == NULL || an->start.line > a->anchors[i].start.line || + (an->start.line == a->anchors[i].start.line && + an->start.pos > a->anchors[i].start.pos)) + an = &a->anchors[i]; + } + } + return an; +} + +Anchor * +closest_prev_anchor(AnchorList * a, Anchor * an, int x, int y) +{ + int i; + + if (a == NULL || a->nanchor == 0) + return an; + for (i = 0; i < a->nanchor; i++) { + if (a->anchors[i].hseq < 0) + continue; + if (a->anchors[i].end.line < y || + (a->anchors[i].end.line == y && a->anchors[i].end.pos <= x)) { + if (an == NULL || an->end.line < a->anchors[i].end.line || + (an->end.line == a->anchors[i].end.line && + an->end.pos < a->anchors[i].end.pos)) + an = &a->anchors[i]; + } + } + return an; +} diff --git a/backend.c b/backend.c new file mode 100644 index 0000000..3befd72 --- /dev/null +++ b/backend.c @@ -0,0 +1,405 @@ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <ctype.h> +#include "fm.h" +#include "gc.h" +#include "terms.h" + + +/* Prototype declaration of internal functions */ +#ifdef HAVE_READLINE +#include <readline/readline.h> +#else /* ! HAVE_READLINE */ +static char *readline( char* ); +#endif /* ! HAVE_READLINE */ +static TextList* split( char * ); + + +/* Prototype declaration of command functions */ +static void get( TextList* ); +static void post( TextList* ); +static void set( TextList* ); +static void show( TextList* ); +static void quit( TextList* ); +static void help( TextList* ); + + +/* Table of command functions */ +struct { + const char *name; + const char *option_string; + const char *help; + void (*func)( TextList* ); +} command_table[] = { + { "get", "[-download_only] URL", "Retrieve URL.", get }, + { "post", "[-download_only] [-target TARGET] [-charset CHARSET]" + " [-enctype ENCTYPE] [-body BODY] [-boundary BOUNDARY] [-length LEN] URL", + "Retrieve URL.", post }, + { "set", "VARIABLE VALUE", "Set VALUE to VARIABLE.", set }, + { "show", "VARIABLE", "Show value of VARIABLE.", show }, + { "quit", "", "Quit program.", quit }, + { "help", "", "Display help messages.", help }, + { NULL, NULL, NULL, NULL }, +}; + + +/* Prototype declaration of functions to manipulate configuration variables */ +static void set_column( TextList* ); +static void show_column( TextList* ); + + +/* Table of configuration variables */ +struct { + const char *name; + void (*set_func)( TextList* ); + void (*show_func)( TextList* ); +} variable_table[] = { + { "column", set_column, show_column }, + { NULL, NULL, NULL }, +}; + + +static char* get_mime_charset_name( int coding ){ + Str r; + switch( coding ){ + case CODE_EUC: + r = Strnew_charp( "euc-japan" ); + break; + case CODE_SJIS: + r = Strnew_charp( "shift_jis" ); + break; + case CODE_JIS_m: + case CODE_JIS_n: + case CODE_JIS_N: + case CODE_JIS_j: + case CODE_JIS_J: + r = Strnew_charp( "iso-2022-jp" ); + break; + default: + return NULL; + } + return r->ptr; +} + + +static void print_headers( Buffer *buf, int len ){ + TextListItem *tp; + + if( buf->document_header ){ + for( tp = buf->document_header->first; tp; tp = tp->next ) + printf( "%s\n", tp->ptr ); + } + printf( "w3m-content-type: %s\n", buf->type ); +#ifdef JP_CHARSET + if( buf->document_code ) + printf( "w3m-content-charset: %s\n", + get_mime_charset_name( buf->document_code ) ); +#endif + if( len > 0 ) + printf( "w3m-content-length: %d\n", len ); +} + + +static void print_formlist( int fid, FormList *fp ){ + Str s = Sprintf( "w3m-form: (formlist (fid %d) (action \"%s\") (method \"%s\")", + fid, + fp->action->ptr, + ( fp->method == FORM_METHOD_POST )? "post" + :( ( fp->method == FORM_METHOD_INTERNAL )? "internal" : "get" ) ); + if( fp->target ) + Strcat( s, Sprintf( " (target \"%s\")", fp->target ) ); + if( fp->charset ) + Strcat( s, Sprintf( " (charset '%s)", get_mime_charset_name(fp->charset) ) ); + if( fp->enctype == FORM_ENCTYPE_MULTIPART ) + Strcat_charp( s, " (enctype \"multipart/form-data\")" ); + if( fp->boundary ) + Strcat( s, Sprintf( " (boundary \"%s\")", fp->boundary ) ); + Strcat_charp( s, ")\n" ); + Strfputs( s, stdout ); +} + + +static void internal_get( char *url, int flag, FormList *request ){ + Buffer *buf; + + backend_halfdump_str = Strnew_charp( "<pre>\n" ); + do_download = flag; + buf = loadGeneralFile( url, NULL, NO_REFERER, 0, request ); + do_download = FALSE; + if( buf != NULL && buf != NO_BUFFER ){ + if( !strcasecmp( buf->type, "text/html" ) ){ + Strcat( backend_halfdump_str, + Sprintf( "</pre><title>%s</title>\n", buf->buffername ) ); + print_headers( buf, backend_halfdump_str->length ); + if( buf->formlist ){ + FormList *fp; + int fid = 0; + for( fp = buf->formlist; fp; fp = fp->next ) fid++; + for( fp = buf->formlist; fp; fp = fp->next ) + print_formlist( --fid, fp ); + } + printf( "\n" ); + Strfputs( backend_halfdump_str, stdout ); + } else { + if( !strcasecmp( buf->type, "text/plain" ) ){ + Line *lp; + int len = 0; + for( lp = buf->firstLine; lp; lp = lp->next ){ + len += lp->len; + if( lp->lineBuf[lp->len-1] != '\n' ) len++; + } + print_headers( buf, len ); + printf( "\n" ); + saveBuffer( buf, stdout ); + } else { + print_headers( buf, 0 ); + } + } + } +} + + +/* Command: get */ +static void get( TextList *argv ){ + char *p, *url = NULL; + int flag = FALSE; + + while(( p = popText( argv ) )){ + if( !strcasecmp( p, "-download_only" ) ) + flag = TRUE; + else + url = p; + } + if( url ){ + internal_get( url, flag, NULL ); + } +} + + +/* Command: post */ +static void post( TextList *argv ){ + FormList *request; + char *p, *target = NULL, *charset = NULL, + *enctype = NULL, *body = NULL, *boundary = NULL, *url = NULL; + int flag = FALSE, length = 0; + + while(( p = popText( argv ) )){ + if( !strcasecmp( p, "-download_only" ) ) + flag = TRUE; + else if( !strcasecmp( p, "-target" ) ) + target = popText( argv ); + else if( !strcasecmp( p, "-charset" ) ) + charset = popText( argv ); + else if( !strcasecmp( p, "-enctype" ) ) + enctype = popText( argv ); + else if( !strcasecmp( p, "-body" ) ) + body = popText( argv ); + else if( !strcasecmp( p, "-boundary" ) ) + boundary = popText( argv ); + else if( !strcasecmp( p, "-length" ) ) + length = atol( popText( argv ) ); + else + url = p; + } + if( url ){ + request = newFormList( NULL, "post", charset, enctype, target, NULL ); + request->body = body; + request->boundary = boundary; + request->length = ( length > 0 )? length : ( body ? strlen(body) : 0 ); + internal_get( url, flag, request ); + } +} + + +/* Command: set */ +static void set( TextList *argv ){ + if( argv->nitem > 1 ){ + int i; + for( i = 0; variable_table[i].name; i++ ){ + if( !strcasecmp( variable_table[i].name, argv->first->ptr ) ){ + popText( argv ); + if( variable_table[i].set_func ) + variable_table[i].set_func( argv ); + break; + } + } + } +} + + +/* Command: show */ +static void show( TextList *argv ){ + if( argv->nitem >= 1 ){ + int i; + for( i = 0; variable_table[i].name; i++ ){ + if( !strcasecmp( variable_table[i].name, argv->first->ptr ) ){ + popText( argv ); + if( variable_table[i].show_func ) + variable_table[i].show_func( argv ); + break; + } + } + } +} + + +/* Command: quit */ +static void quit( TextList *argv ){ + deleteFiles(); +#ifdef USE_COOKIE + save_cookies(); +#endif /* USE_COOKIE */ + exit(0); +} + + +/* Command: help */ +static void help( TextList *argv ){ + int i; + for( i = 0; command_table[i].name; i++ ) + printf( "%s %s\n %s\n", + command_table[i].name, + command_table[i].option_string, + command_table[i].help ); +} + + +/* Sub command: set COLS */ +static void set_column( TextList *argv ){ + if( argv->nitem == 1 ){ + COLS = atol( argv->first->ptr ); + } +} + +/* Sub command: show COLS */ +static void show_column( TextList *argv ){ + fprintf( stdout, "column=%d\n", COLS ); +} + + +/* Call appropriate command function based on given string */ +static void call_command_function( char *str ){ + int i; + TextList *argv = split( str ); + if( argv->nitem > 0 ){ + for( i = 0; command_table[i].name; i++ ){ + if( !strcasecmp( command_table[i].name, argv->first->ptr ) ){ + popText( argv ); + if( command_table[i].func ) + command_table[i].func( argv ); + break; + } + } + } +} + + +/* Main function */ +int backend( void ){ + char *str; + extern int w3m_dump; /* Declared in main.c. */ + + w3m_dump = FALSE; + w3m_halfdump = FALSE; + if (COLS == 0) COLS = 80; +#ifdef MOUSE + mouse_end(); + use_mouse = FALSE; +#endif /* MOUSE */ + + if( backend_batch_commands ){ + while(( str = popText(backend_batch_commands) )) + call_command_function( str ); + } else { + while(( str = readline( "w3m> " ) )) + call_command_function( str ); + } + quit( NULL ); + return 0; +} + + +/* Dummy function of readline(). */ +#ifndef HAVE_READLINE +static char *readline( char *prompt ){ + Str s; + fputs( prompt, stdout ); + fflush( stdout ); + s = Strfgets( stdin ); + if( feof( stdin ) && (strlen( s->ptr ) == 0) ) + return NULL; + else + return s->ptr; +} +#endif /* ! HAVE_READLINE */ + + +/* Splits a string into a list of tokens and returns that list. */ +static TextList* split( char *p ){ + int in_double_quote = FALSE, in_single_quote = FALSE; + Str s = Strnew(); + TextList* tp = newTextList(); + + for( ; *p; p++ ){ + switch( *p ){ + case '"': + if( in_single_quote ) + Strcat_char( s, '"' ); + else + in_double_quote = !in_double_quote; + break; + case '\'': + if( in_double_quote ) + Strcat_char( s, '\'' ); + else + in_single_quote = !in_single_quote; + break; + case '\\': + if( !in_single_quote ){ + /* Process escape characters. */ + p++; + switch( *p ){ + case 't': + Strcat_char( s, '\t' ); + break; + case 'r': + Strcat_char( s, '\r' ); + break; + case 'f': + Strcat_char( s, '\f' ); + break; + case 'n': + Strcat_char( s, '\n' ); + break; + case '\0': + goto LAST; + default: + Strcat_char( s, *p ); + } + } else { + Strcat_char( s, *p ); + } + break; + case ' ': + case '\t': + case '\r': + case '\f': + case '\n': + /* Separators are detected. */ + if( in_double_quote || in_single_quote ){ + Strcat_char( s, *p ); + } else if( s->length > 0 ){ + pushText( tp, s->ptr ); + s = Strnew(); + } + break; + default: + Strcat_char( s, *p ); + } + } +LAST: + if( s->length > 0 ) + pushText( tp, s->ptr ); + return tp; +} diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..4ea4982 --- /dev/null +++ b/buffer.c @@ -0,0 +1,676 @@ +#include "fm.h" + +#ifdef MOUSE +#ifdef USE_GPM +#include <gpm.h> +#endif +#if defined(USE_GPM) || defined(USE_SYSMOUSE) +extern int do_getch(); +#define getch() do_getch() +#endif /* USE_GPM */ +#endif /* MOUSE */ + +#ifdef __EMX__ +#include <sys/kbdscan.h> +#include <strings.h> +#endif +char *NullLine = ""; +Lineprop NullProp[] = +{0}; + +/* + * Buffer creation + */ +Buffer * +newBuffer(int width) +{ + Buffer *n; + + n = New(Buffer); + if (n == NULL) + return NULL; + bzero((void *) n, sizeof(Buffer)); + n->width = width; + n->currentURL.scheme = SCM_UNKNOWN; + n->baseURL = NULL; + n->baseTarget = NULL; + n->buffername = ""; + n->bufferprop = BP_NORMAL; + n->clone = New(int); + *n->clone = 1; + n->linelen = 0; + n->trbyte = 0; +#ifdef USE_SSL + n->ssl_certificate = NULL; +#endif + return n; +} + +/* + * Create null buffer + */ +Buffer * +nullBuffer(void) +{ + Buffer *b; + + b = newBuffer(COLS); + b->buffername = "*Null*"; + return b; +} + +/* + * clearBuffer: clear buffer content + */ +void +clearBuffer(Buffer * buf) +{ + buf->firstLine = buf->topLine = buf->currentLine = buf->lastLine = NULL; + buf->allLine = 0; +} + +/* + * discardBuffer: free buffer structure + */ + +void +discardBuffer(Buffer * buf) +{ + int i; + Buffer *b; + + clearBuffer(buf); + for (i = 0; i < MAX_LB; i++) { + b = buf->linkBuffer[i]; + if (b == NULL) + continue; + b->linkBuffer[REV_LB[i]] = NULL; + } + if (buf->savecache) + unlink(buf->savecache); + if (--(*buf->clone)) + return; + if (buf->pagerSource) + ISclose(buf->pagerSource); + if (buf->sourcefile) { + if (buf->real_scheme != SCM_LOCAL || buf->bufferprop & BP_FRAME) + unlink(buf->sourcefile); + } + while (buf->frameset) { + deleteFrameSet(buf->frameset); + buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL); + } +} + +/* + * namedBuffer: Select buffer which have specified name + */ +Buffer * +namedBuffer(Buffer * first, char *name) +{ + Buffer *buf; + + if (!strcmp(first->buffername, name)) { + return first; + } + for (buf = first; buf->nextBuffer != NULL; buf = buf->nextBuffer) { + if (!strcmp(buf->nextBuffer->buffername, name)) { + return buf->nextBuffer; + } + } + return NULL; +} + +/* + * deleteBuffer: delete buffer + */ +Buffer * +deleteBuffer(Buffer * first, Buffer * delbuf) +{ + Buffer *buf, *b; + + if (first == delbuf && first->nextBuffer != NULL) { + buf = first->nextBuffer; + discardBuffer(first); + return buf; + } + if ((buf = prevBuffer(first, delbuf)) != NULL) { + b = buf->nextBuffer; + buf->nextBuffer = b->nextBuffer; + discardBuffer(b); + } + return first; +} + +/* + * replaceBuffer: replace buffer + */ +Buffer * +replaceBuffer(Buffer * first, Buffer * delbuf, Buffer * newbuf) +{ + Buffer *buf; + + if (delbuf == NULL) { + newbuf->nextBuffer = first; + return newbuf; + } + if (first == delbuf) { + newbuf->nextBuffer = delbuf->nextBuffer; + discardBuffer(delbuf); + return newbuf; + } + if (delbuf && (buf = prevBuffer(first, delbuf))) { + buf->nextBuffer = newbuf; + newbuf->nextBuffer = delbuf->nextBuffer; + discardBuffer(delbuf); + return first; + } + newbuf->nextBuffer = first; + return newbuf; +} + +Buffer * +nthBuffer(Buffer * firstbuf, int n) +{ + int i; + Buffer *buf = firstbuf; + + if (n < 0) + return firstbuf; + for (i = 0; i < n; i++) { + if (buf == NULL) + return NULL; + buf = buf->nextBuffer; + } + return buf; +} + +static void +writeBufferName(Buffer * buf, int n) +{ + Str msg; + int all; + + all = buf->allLine; + if (all == 0 && buf->lastLine != NULL) + all = buf->lastLine->linenumber; + move(n, 0); + msg = Sprintf("<%s> [%d lines]", buf->buffername, all); + if (buf->filename != NULL) { + switch (buf->currentURL.scheme) { + case SCM_LOCAL: + case SCM_LOCAL_CGI: + if (strcmp(buf->currentURL.file, "-")) { + Strcat_char(msg, ' '); + Strcat_charp(msg, buf->filename); + } + break; + case SCM_UNKNOWN: + case SCM_MISSING: + break; + default: + Strcat_char(msg, ' '); + Strcat(msg, parsedURL2Str(&buf->currentURL)); + break; + } + } + addnstr_sup(msg->ptr, COLS - 1); +} + + +/* + * gotoLine: go to line number + */ +void +gotoLine(Buffer * buf, int n) +{ + char msg[32]; + Line *l = buf->firstLine; + + if (l == NULL) + return; + if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) { + if (buf->lastLine->linenumber < n) + getNextPage(buf, n - buf->lastLine->linenumber); + while ((buf->lastLine->linenumber < n) && + (getNextPage(buf, 1) != NULL)); + } + if (l->linenumber > n) { + sprintf(msg, "First line is #%ld", l->linenumber); + disp_message(msg, FALSE); + buf->topLine = buf->currentLine = l; + return; + } + if (buf->lastLine->linenumber < n) { + l = buf->lastLine; + sprintf(msg, "Last line is #%ld", buf->lastLine->linenumber); + disp_message(msg, FALSE); + buf->currentLine = l; + buf->topLine = lineSkip(buf, buf->currentLine, - (LASTLINE - 1), FALSE); + return; + } + for (; l != NULL; l = l->next) { + if (l->linenumber >= n) { + buf->currentLine = l; + if (n < buf->topLine->linenumber || + buf->topLine->linenumber + LASTLINE <= n) + buf->topLine = lineSkip(buf, l, -(LASTLINE + 1) / 2, FALSE); + break; + } + } +} + +/* + * gotoRealLine: go to real line number + */ +void +gotoRealLine(Buffer * buf, int n) +{ + char msg[32]; + Line *l = buf->firstLine; + + if (l == NULL) + return; + if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) { + if (buf->lastLine->real_linenumber < n) + getNextPage(buf, n - buf->lastLine->real_linenumber); + while ((buf->lastLine->real_linenumber < n) && + (getNextPage(buf, 1) != NULL)); + } + if (l->real_linenumber > n) { + sprintf(msg, "First line is #%ld", l->real_linenumber); + disp_message(msg, FALSE); + buf->topLine = buf->currentLine = l; + return; + } + if (buf->lastLine->real_linenumber < n) { + l = buf->lastLine; + sprintf(msg, "Last line is #%ld", buf->lastLine->real_linenumber); + disp_message(msg, FALSE); + buf->currentLine = l; + buf->topLine = lineSkip(buf, buf->currentLine, - (LASTLINE - 1), FALSE); + return; + } + for (; l != NULL; l = l->next) { + if (l->real_linenumber >= n) { + buf->currentLine = l; + if (n < buf->topLine->real_linenumber || + buf->topLine->real_linenumber + LASTLINE <= n) + buf->topLine = lineSkip(buf, l, -(LASTLINE + 1) / 2, FALSE); + break; + } + } +} + + +static Buffer * +listBuffer(Buffer * top, Buffer * current) +{ + int i, c = 0; + Buffer *buf = top; + + move(0, 0); +#ifdef COLOR + if (useColor) { + setfcolor(basic_color); +#ifdef BG_COLOR + setbcolor(bg_color); +#endif /* BG_COLOR */ + } +#endif /* COLOR */ + clrtobotx(); + for (i = 0; i < LASTLINE; i++) { + if (buf == current) { + c = i; + standout(); + } + writeBufferName(buf, i); + if (buf == current) { + standend(); + clrtoeolx(); + move(i, 0); + toggle_stand(); + } + else + clrtoeolx(); + if (buf->nextBuffer == NULL) { + move(i + 1, 0); + clrtobotx(); + break; + } + buf = buf->nextBuffer; + } + standout(); + message("Buffer selection mode: SPC for select / D for delete buffer", 0, 0); + standend(); +/* + * move(LASTLINE, COLS - 1); */ + move(c, 0); + refresh(); + return buf->nextBuffer; +} + + +/* + * Select buffer visually + */ +Buffer * +selectBuffer(Buffer * firstbuf, Buffer * currentbuf, char *selectchar) +{ + int i, cpoint, /* Current Buffer Number */ + spoint, /* Current Line on Screen */ + maxbuf, sclimit = LASTLINE; /* Upper limit of line * number in + * the * screen */ + Buffer *buf, *topbuf; + char c; + + i = cpoint = 0; + for (buf = firstbuf; buf != NULL; buf = buf->nextBuffer) { + if (buf == currentbuf) + cpoint = i; + i++; + } + maxbuf = i; + + if (cpoint >= sclimit) { + spoint = sclimit / 2; + topbuf = nthBuffer(firstbuf, cpoint - spoint); + } + else { + topbuf = firstbuf; + spoint = cpoint; + } + listBuffer(topbuf, currentbuf); + + for (;;) { + if ((c = getch()) == ESC_CODE) { + if ((c = getch()) == '[' || c == 'O') { + switch (c = getch()) { + case 'A': + c = 'k'; + break; + case 'B': + c = 'j'; + break; + case 'C': + c = ' '; + break; + case 'D': + c = 'B'; + break; + } + } + } +#ifdef __EMX__ + else if(!c) + switch(getch()){ + case K_UP: + c='k'; + break; + case K_DOWN: + c='j'; + break; + case K_RIGHT: + c=' '; + break; + case K_LEFT: + c='B'; + } +#endif + switch (c) { + case CTRL_N: + case 'j': + if (spoint < sclimit - 1) { + if (currentbuf->nextBuffer == NULL) + continue; + writeBufferName(currentbuf, spoint); + currentbuf = currentbuf->nextBuffer; + cpoint++; + spoint++; + standout(); + writeBufferName(currentbuf, spoint); + standend(); + move(spoint, 0); + toggle_stand(); + } + else if (cpoint < maxbuf - 1) { + topbuf = currentbuf; + currentbuf = currentbuf->nextBuffer; + cpoint++; + spoint = 1; + listBuffer(topbuf, currentbuf); + } + break; + case CTRL_P: + case 'k': + if (spoint > 0) { + writeBufferName(currentbuf, spoint); + currentbuf = nthBuffer(topbuf, --spoint); + cpoint--; + standout(); + writeBufferName(currentbuf, spoint); + standend(); + move(spoint, 0); + toggle_stand(); + } + else if (cpoint > 0) { + i = cpoint - sclimit; + if (i < 0) + i = 0; + cpoint--; + spoint = cpoint - i; + currentbuf = nthBuffer(firstbuf, cpoint); + topbuf = nthBuffer(firstbuf, i); + listBuffer(topbuf, currentbuf); + } + break; + default: + *selectchar = c; + return currentbuf; + } +/* + * move(LASTLINE, COLS - 1); + */ + move(spoint, 0); + refresh(); + } +} + + +/* + * Reshape HTML buffer + */ +void +reshapeBuffer(Buffer * buf) +{ + URLFile f; + int top, linenum, cursorY, pos, currentColumn; + AnchorList *formitem; + + init_stream(&f, SCM_LOCAL, NULL); + examineFile(buf->sourcefile, &f); + if (f.stream == NULL) + return; + + if (buf->firstLine == NULL) { + top = 1; + linenum = 1; + } else { + top = buf->topLine->linenumber; + linenum = buf->currentLine->linenumber; + } + cursorY = buf->cursorY; + pos = buf->pos; + currentColumn = buf->currentColumn; + clearBuffer(buf); + while (buf->frameset) { + deleteFrameSet(buf->frameset); + buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL); + } + + formitem = buf->formitem; + buf->href = NULL; + buf->name = NULL; + buf->img = NULL; + buf->formitem = NULL; + buf->width = INIT_BUFFER_WIDTH; + + loadHTMLstream(&f, buf, NULL, FALSE); + UFclose(&f); + + buf->topLine = buf->firstLine; + buf->lastLine = buf->currentLine; + buf->currentLine = buf->firstLine; + buf->height = LASTLINE + 1; + buf->topLine = lineSkip(buf, buf->topLine, top - 1, FALSE); + gotoLine(buf, linenum); + buf->pos = pos; + buf->currentColumn = currentColumn; + arrangeCursor(buf); + if (buf->check_url & CHK_URL) + chkURL(); +#ifdef USE_NNTP + if (buf->check_url & CHK_NMID) + chkNMID(); +#endif + formResetBuffer(buf, formitem); +} + +/* shallow copy */ +void +copyBuffer(Buffer * a, Buffer * b) +{ + readBufferCache(b); + bcopy((void *) b, (void *) a, sizeof(Buffer)); +} + +Buffer * +prevBuffer(Buffer * first, Buffer * buf) +{ + Buffer *b; + + for (b = first; b != NULL && b->nextBuffer != buf; b = b->nextBuffer); + return b; +} + +#define fwrite1(d, f) (fwrite(&d, sizeof(d), 1, f)==0) +#define fread1(d, f) (fread(&d, sizeof(d), 1, f)==0) + +int +writeBufferCache(Buffer *buf) +{ + Str tmp; + FILE *cache = NULL; + Line *l; +#ifdef ANSI_COLOR + int colorflag; +#endif + + if (buf->savecache) + return -1; + + if (buf->firstLine == NULL) + goto _error1; + + tmp = tmpfname(TMPF_CACHE, NULL); + buf->savecache = tmp->ptr; + cache = fopen(buf->savecache, "w"); + if (!cache) + goto _error1; + + if (fwrite1(buf->currentLine->linenumber, cache) || + fwrite1(buf->topLine->linenumber, cache)) + goto _error; + + for (l = buf->firstLine; l; l = l->next) { + if (fwrite1(l->real_linenumber, cache) || + fwrite1(l->usrflags, cache) || + fwrite1(l->width, cache) || + fwrite1(l->len, cache) || + fwrite(l->lineBuf, 1, l->len, cache) < l->len || + fwrite(l->propBuf, sizeof(Lineprop), l->len, cache) < l->len) + goto _error; +#ifdef ANSI_COLOR + colorflag = l->colorBuf ? 1 : 0; + if (fwrite1(colorflag, cache)) + goto _error; + if (colorflag) { + if (fwrite(l->colorBuf, sizeof(Linecolor), l->len, cache) < l->len) + goto _error; + } +#endif + } + + fclose(cache); + return 0; + _error: + fclose(cache); + unlink(buf->savecache); + _error1: + buf->savecache = NULL; + return -1; +} + +int +readBufferCache(Buffer *buf) +{ + FILE *cache; + Line *l = NULL, *prevl; + long lnum = 0, clnum, tlnum; +#ifdef ANSI_COLOR + int colorflag; +#endif + + if (buf->savecache == NULL) + return -1; + + cache = fopen(buf->savecache, "r"); + if (cache == NULL || + fread1(clnum, cache) || + fread1(tlnum, cache)) { + buf->savecache = NULL; + return -1; + } + + while (!feof(cache)) { + lnum++; + prevl = l; + l = New(Line); + l->prev = prevl; + if (prevl) + prevl->next = l; + else + buf->firstLine = l; + l->linenumber = lnum; + if (lnum == clnum) + buf->currentLine = l; + if (lnum == tlnum) + buf->topLine = l; + if (fread1(l->real_linenumber, cache) || + fread1(l->usrflags, cache) || + fread1(l->width, cache) || + fread1(l->len, cache)) + break; + l->lineBuf = New_N(char, l->len + 1); + fread(l->lineBuf, 1, l->len, cache); + l->lineBuf[l->len] = '\0'; + l->propBuf = New_N(Lineprop, l->len); + fread(l->propBuf, sizeof(Lineprop), l->len, cache); +#ifdef ANSI_COLOR + if (fread1(colorflag, cache)) + break; + if (colorflag) { + l->colorBuf = New_N(Linecolor, l->len); + fread(l->colorBuf, sizeof(Linecolor), l->len, cache); + } else { + l->colorBuf = NULL; + } +#endif + } + buf->lastLine = prevl; + buf->lastLine->next = NULL; + fclose(cache); + unlink(buf->savecache); + buf->savecache = NULL; + return 0; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..db8e478 --- /dev/null +++ b/config.h @@ -0,0 +1,201 @@ +/* + * Configuration for w3m + */ + +#ifndef _CONFIGURED_ +#define _CONFIGURED_ + +/* User Configuration */ + +/* + If you define DICT, you can use dictionary look-up function + in w3m. See README.dict for detail. +*/ +#undef DICT + +/* + If you define USE_MARK, you can use set-mark (C-SPC), + goto-next-mark (ESC p), goto-next-mark (ESC n) and + mark-by-regexp ("). +*/ +#undef USE_MARK + +/* + If you want to load and save URL history. + */ +#define USE_HISTORY + +/* + BG_COLOR enables w3m to set background color. + */ +#define BG_COLOR + +/* + VIEW_UNSEENOBJECTS enables w3m to make a link to unseen objects. + e.g. background image. + */ +#undef VIEW_UNSEENOBJECTS + +/* + VI_PREC_NUM enables vi-like behavior for '2 SPC' or '2 b' + */ +#undef VI_PREC_NUM + +/* + * Do word fill + */ +#undef FORMAT_NICE + +/* + * Support Gopher protocol + */ +#undef USE_GOPHER + +/* + * Support NNTP + */ +#define USE_NNTP + +/* + * Support ANSI color escape sequences + */ +#define ANSI_COLOR + +/* + * Enable id attribute + */ +#define ID_EXT + +/* + * Save Current-buffer Information + */ +#define BUFINFO + +/* + * Support EGD (Entropy Gathering Daemon) + */ +#undef USE_EGD + +/* + * MENU_MAP enables w3m to show image map link with popup menu. + */ +#define MENU_MAP + +/* + * Use Emacs-like key binding for file name completion + */ +#undef EMACS_LIKE_LINEEDIT + +/* + * Remove line trailing spaces in html buffer. + */ +#undef ENABLE_REMOVE_TRAILINGSPACES + +/**********************************************************/ +#ifdef makefile_parameter + +BIN_DIR = /usr/local/bin +HELP_DIR = /usr/local/lib/w3m +LIB_DIR = /usr/local/lib/w3m +HELP_FILE = w3mhelp-w3m_ja.html +SYS_LIBRARIES = -lgpm -lbsd -lnsl -ltermcap -L/usr/local/ssl/lib -lssl -lcrypto +LOCAL_LIBRARIES = +CC = gcc +MYCFLAGS = -g -Wall -I./gc/include -I/usr/local/ssl/include/openssl -I/usr/local/ssl/include +GCCFLAGS = -g -Wall -I./gc/include -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DSILENT -DNO_DEBUGGING #-DNO_SIGNALS +KEYBIND_SRC = keybind.c +KEYBIND_OBJ = keybind.o +EXT= +MATHLIB=-lm +GCLIB=gc/gc.a +GCTARGET=gc/gc.a +RANLIB=ranlib +MKDIR=mkdir -p +VERSION=0.2.1 +MODEL=Linux.i686-monster-ja +#else + + +#define DISPLAY_CODE 'E' + +#define JA 0 +#define EN 1 +#define LANG JA +#define KANJI_SYMBOLS +#define COLOR +#define MOUSE +#define USE_GPM +#undef USE_SYSMOUSE +#define MENU +#define USE_COOKIE +#define USE_SSL +#define USE_SSL_VERIFY +#define FTPPASS_HOSTNAMEGEN +#define SHOW_PARAMS + +#define DEF_EDITOR "/bin/vi" +#define DEF_MAILER "/bin/mail" +#define DEF_EXT_BROWSER "/usr/X11R6/bin/netscape" + +#define LIB_DIR "/usr/local/lib/w3m" +#define HELP_DIR "/usr/local/lib/w3m" +#define HELP_FILE "w3mhelp.html" +#define W3MCONFIG "w3mconfig" + +#define RC_DIR "~/.w3m/" +#define BOOKMARK "bookmark.html" +#define CONFIG_FILE "config" +#define KEYMAP_FILE "keymap" +#define MENU_FILE "menu" +#define COOKIE_FILE "cookie" +#define HISTORY_FILE "history" + +#define USER_MAILCAP RC_DIR "/mailcap" +#define SYS_MAILCAP "/etc/mailcap" +#define USER_MIMETYPES "~/.mime.types" +#define SYS_MIMETYPES "/usr/lib/mime.types" + +#define DEF_SAVE_FILE "index.html" + +#define TERMIOS +#define DIRENT +#define STRCASECMP +#define STRCHR +#define STRERROR +#define SYS_ERRLIST +#undef NOBCOPY +#define HAVE_WAITPID +#define HAVE_WAIT3 +#define HAVE_STRFTIME + +#define GETCWD +#define GETWD +#define READLINK +#define HAVE_SETENV +#define HAVE_PUTENV +#define READLINK + + +#define SETJMP(env) sigsetjmp(env,1) +#define LONGJMP(env,val) siglongjmp(env,val) +#define JMP_BUF sigjmp_buf + +typedef void MySignalHandler; +#define SIGNAL_ARG int _dummy +#define SIGNAL_ARGLIST 0 +#define SIGNAL_RETURN return +/* + If you want to use IPv6, define this symbol. + */ +#undef INET6 + +#undef TABLE_EXPAND +#undef TABLE_NO_COMPACT +#define NOWRAP 1 +#define NEW_FORM 1 +#define MATRIX 1 +#undef NO_FLOAT_H + +#endif /* makefile_parameter */ +#endif /* _CONFIGURED_ */ + diff --git a/configure b/configure new file mode 100755 index 0000000..be39e44 --- /dev/null +++ b/configure @@ -0,0 +1,1712 @@ +#!/bin/sh +# +# Configuration. +# + +# +sysname=`uname -s` +sysversion=`uname -r` +host=`hostname` +platform=`uname -m` + +sysversion1=`echo $sysversion | awk -F. '{print $1}'` +sysversion2=`echo $sysversion | awk -F. '{print $2}'` +sysversion3=`echo $sysversion | awk -F. '{print $3}'` + +echo $sysname $sysversion1 $sysversion2 $sysversion3 /$platform at $host + +if [ -f config.param ] ; then + confhost=`awk 'NR==1{print $4}' config.param` + if [ "$confhost" = "$host" ] ; then + . ./config.param + fi +fi + +echo "# Configuretion at $host" > config.param + +# parameters: + +prefix=/usr/local +all_yes=0 +while [ $# -gt 0 ] +do + case "$1" in + -yes|--yes|-nonstop|--nonstop) + all_yes=1 + echo "Setting all parameters to the default..." + ;; + -prefix|--prefix) + prefix=$2 + shift + ;; + -prefix=*|--prefix=*) + prefix=`expr "$1" : "-*prefix=\(.*\)"` + ;; + -lang=en|--lang=en) + pref_lang=2 + ;; + -lang=ja|--lang=ja) + pref_lang=1 + ;; + -model=baby|--model=baby) + dmodel=1 + ;; + -model=little|--model=little) + dmodel=2 + ;; + -model=mouse|--model=mouse) + dmodel=3 + ;; + -model=cookie|--model=cookie) + dmodel=4 + ;; + -model=monster|--model=monster) + dmodel=5 + ;; + -code=*|--code=*) + def_dcode=`expr "$1" : "-*code=\(.*\)"` + ;; + -cflags=*|--cflags=*) + dcflags=`echo $1 | sed -e 's/-*cflags=//'` + ;; + -help|--help) + echo "-yes, -nonstop Set all parameters to the default" + echo "-prefix=DIR Specify prefix (default: /usr/local)" + echo "-lang=(en|ja) Specify default language" + echo "-model=(baby|little|mouse|cookie|monster)" + echo " Specify default model" + echo "-code=(S|E|j|N|n|m)" + echo " Specify derault kanji code" + echo "-cflags=FLAGS Specify C flags" + echo "-help Display help" + exit 0 + ;; + esac + shift +done + +# Version number of Boehm-GC library comes with w3m. +# version number: JMMAAA J: major MM: minor AAA: alpha +# Alpha number of non-alpha version is 255. +# version 4.14alpha1 => 414002 +mygcversion=500003 + +if [ -z "`echo -n aho | grep n`" ] ; then +Echo() +{ + echo -n "$*" +} +else +Echo() +{ + echo "$*\c" +} +fi + + +do_sigtest() +{ + echo "#include <signal.h>" > _zmachdep.c + if [ "$2" = void ]; then + echo "$1 _handler($2) {}" >> _zmachdep.c + else + echo "$1 _handler($2 x) {}" >> _zmachdep.c + fi + echo "int main(void) { $1 (*hdl)($2); hdl = signal(SIGINT,_handler); return 0; }" >> _zmachdep.c + $cc $cflags -o _zmachdep _zmachdep.c > _zwarning 2>&1 + stat=$? + warning=`cat _zwarning` + rm -f _zwarning +} + +readdir() { + if [ "$all_yes" = 0 ]; then + read __dir + else + __dir=$1 + echo "$1" + fi + if [ -z "$__dir" ]; then + _dir=$1 + else + _dir=`echo "$__dir"|sed -e "s;^~;$HOME;"` + fi +} + +readanswer() { + var=$1 + dflt=$2 + ok=$3 + if [ "$all_yes" = 0 -o -z "$dflt$ok" ]; then + read ans + if [ -z "$ans" ]; then + ans=$dflt + fi + else + ans=$dflt + echo "$ans" + fi + eval $var='$ans' +} + +yesno() { + var=$1 + dflt=$2 + ddflt=$3 + if [ -z "$dflt" ]; then + dflt=$ddflt + fi + if [ "$dflt" = y ]; then + ndflt=n + else + ndflt=y + fi + Echo "[$dflt]? " + if [ "$all_yes" = 0 ]; then + read ks_ans + else + ks_ans=$dflt + echo "$dflt" + fi + if [ "$ks_ans" = "$ndflt" ]; then + eval $var='$ndflt' + else + eval $var='$dflt' + fi +} + +save_params() { + echo "use_color=$use_color" >> config.param + echo "use_menu=$use_menu" >> config.param + echo "use_mouse=$use_mouse" >> config.param + echo "use_cookie=$use_cookie" >> config.param + echo "use_ssl=$use_ssl" >> config.param + if [ -n "$dmodel" ]; then + echo "dmodel=$dmodel" >> config.param + fi +} + +find_ssl() { + sslinclude="" + for i1 in /usr /usr/local + do + for i2 in /openssl /ssl / + do + if [ "$i2" = "/" ]; then i2=''; fi + dirname=${i1}${i2} + if [ -f $dirname/include/ssl.h ]; then + sslinclude="-I${dirname}/include" + elif [ -f $dirname/include/openssl/ssl.h ]; then + sslinclude="-I${dirname}/include/openssl -I${dirname}/include" + fi + for i3 in lib/openssl lib + do + dirname=${i1}${i2}/${i3} + for ext in a so + do + if [ -f $dirname/libssl.$ext -o -f $dirname/libcrypto.$ext ]; then + if [ "$ssllib" = -L${dirname} ]; then + ssllib="-L${dirname}" + else + ssllib="$ssllib -L${dirname}" + fi + fi + done + done + done + done + ssllib="$ssllib -lssl -lcrypto" + if [ "$sslinclude" = "" ]; then + echo "Where is ssl.h? (for example, /usr/crypto/include)" + Echo ":" + read ks_ans + sslinclude="-I${ks_ans}" + if [ -d $ks_ans/openssl ]; then + sslinclude="${sslinclude} -I${ks_ans}/openssl" + fi + echo "Where is libssl.a? (for example, /usr/crypto/lib)" + Echo ":" + read ks_ans + ssllib="-L${ks_ans} -lssl -lcrypto" + fi +} + +#-------------------------------------------------------------- +if [ -n "$USER" ]; then + user=$USER +elif [ -n "$LOGNAME" ]; then + user=$LOGNAME +elif [ -n "`whoami`" ]; then + user=`whoami` +else +# Echo "Who are you? " +# read user + user=nobody +fi + +echo "%" +echo "% Hello $user. Let's start configuration process for w3m." +echo "% Please answer some questions." +echo "%" + +if [ -n "`echo $sysname | grep CYGWIN`" ]; then + sysname="CYGWIN" + extension='.exe' +else + extension= +fi + +topdir=$prefix +special_sys='' +case "$sysname" in + aix | AIX ) + special_sys="#define AIX" + ;; + CYGWIN ) + special_sys="#define CYGWIN $sysversion1" + if [ $sysversion1 -eq 0 ]; then + topdir=/cygnus/cygwin-b20/H-i586-cygwin32 + fi + ;; + NetBSD ) +# Newer NetBSD system doesn't define 'unix' symbol anymore, but GC library +# requires it. + special_sys="#define unix" + ;; +esac + +if [ -z "$def_bindir" ]; then + def_bindir="$topdir/bin" +fi +echo "Which directory do you want to put the binary?" +Echo "(default: $def_bindir) " +readdir "$def_bindir" +bindir=$_dir +echo "def_bindir='$bindir'" >> config.param + +if [ -z "$def_libdir" ]; then + case "$sysname" in + *BSD) + def_libdir="$topdir/libexec/w3m" + ;; + *) + def_libdir="$topdir/lib/w3m" + ;; + esac +fi +echo "Which directory do you want to put the support binary files?" +Echo "(default: $def_libdir) " +readdir "$def_libdir" +suplibdir=$_dir +echo "def_libdir='$suplibdir'" >> config.param + +if [ -z "$def_helpdir" ]; then + def_helpdir="$topdir/lib/w3m" +fi +echo "Which directory do you want to put the helpfile?" +Echo "(default: $def_helpdir) " +readdir "$def_helpdir" +helpdir=$_dir +echo "def_helpdir='$helpdir'" >> config.param + +echo "Which language do you prefer?" +echo " 1 - Japanese (charset ISO-2022-JP, EUC-JP, Shift_JIS)" +echo " 2 - English (charset US_ASCII, ISO-8859-1, etc.)" +if [ "$pref_lang" = 2 ]; then + Echo '[2]? ' + def_lg=2 +else + Echo '[1]? ' + def_lg=1 +fi +while : +do + readanswer lg_ans "$def_lg" + if [ "$lg_ans" != 1 -a "$lg_ans" != 2 ]; then + echo "Please choose 1 or 2." + Echo "[$def_lg]? " + continue + else + : + fi + break +done +echo "pref_lang=$lg_ans" >> config.param +if [ "$lg_ans" = 1 ]; then + use_lang="#define LANG JA" + lang=ja +else + use_lang="#define LANG EN" + lang=en +fi + + +if [ "$lang" = ja ]; then + echo "Input your display kanji code." + echo " S - Shift JIS" + echo " E - EUC-JP" + echo ' j - JIS: ESC $@ - ESC (J' + echo ' N - JIS: ESC $B - ESC (J' + echo ' n - JIS: ESC $B - ESC (B' + echo ' m - JIS: ESC $@ - ESC (B' + echo '' + while : + do + if [ -n "$def_dcode" ] ; then + Echo "(default: $def_dcode) " + fi + Echo "Which? " + readanswer ncode "$def_dcode" + case "$ncode" in + [SEjNnm]) + ;; + *) + echo "Illegal code. Try again." + continue + ;; + esac + break + done + echo "def_dcode=$ncode" >> config.param +else + ncode=x +fi + +echo "Do you want to use Lynx-like key binding?" +yesno lynx_key "$lynx_key" n +echo "lynx_key=$lynx_key" >> config.param +if [ "$lynx_key" = y ]; then + keymap_file="keybind_lynx" +else + keymap_file="keybind" +fi + +if [ "$lang" = ja ]; then + if [ "$lynx_key" = y ]; then + helpfile="w3mhelp-lynx_ja.html" + else + helpfile="w3mhelp-w3m_ja.html" + fi +else + if [ "$lynx_key" = y ]; then + helpfile="w3mhelp-lynx_en.html" + else + helpfile="w3mhelp-w3m_en.html" + fi +fi + +if [ "$lang" = ja ]; then + echo "Do you want to use 2-byte character for table border, item, etc." + yesno kanji_symbols "$kanji_symbols" y + echo "kanji_symbols=$kanji_symbols" >> config.param +else + kanji_symbols=n +fi +if [ "$kanji_symbols" = y ]; then + def_kanji_symbols="#define KANJI_SYMBOLS" +else + def_kanji_symbols="#undef KANJI_SYMBOLS" +fi + +echo "Do you want to automatically generate domain parts of passwords for anonymous FTP logins" +yesno ftppass_hostnamegen "$ftppass_hostnamegen" n +echo "ftppass_hostnamegen=$ftppass_hostnamegen" >> config.param +if [ "$ftppass_hostnamegen" = y ]; then + def_ftppass_hostnamegen="#define FTPPASS_HOSTNAMEGEN" +else + def_ftppass_hostnamegen="#undef FTPPASS_HOSTNAMEGEN" +fi + +echo "Do you want listing of options" +yesno show_params "$show_params" n +echo "show_params=$show_params" >> config.param +if [ "$show_params" = y ]; then + def_show_params="#define SHOW_PARAMS" +else + def_show_params="#undef SHOW_PARAMS" +fi + +echo "Do you want NNTP support" +yesno use_nntp "$use_nntp" n +echo "use_nntp=$use_nntp" >> config.param +if [ "$use_nntp" = y ]; then + def_use_nntp="#define USE_NNTP" +else + def_use_nntp="#undef USE_NNTP" +fi + +echo "Do you want ANSI color escape sequences supprot?" +yesno ansi_color "$ansi_color" n +echo "ansi_color=$ansi_color" >> config.param +if [ "$ansi_color" = y ]; then + def_ansi_color="#define ANSI_COLOR" +else + def_ansi_color="#undef ANSI_COLOR" +fi + +echo "" +echo "Let's do some configurations. Choose config option among the list." +echo "" +echo "1 - Baby model (no color, no menu, no mouse, no cookie, no SSL)" +echo "2 - Little model (color, menu, no mouse, no cookie, no SSL)" +echo "3 - Mouse model (color, menu, mouse, no cookie, no SSL)" +echo "4 - Cookie model (color, menu, mouse, cookie, no SSL)" +echo "5 - Monster model (with everything; you need openSSL library)" +echo "6 - Customize" +echo "" +Echo "Which? " +if [ -n "$dmodel" ]; then + Echo "(default: $dmodel) " +fi + +while : +do +readanswer ans "$dmodel" +if [ -z "$ans" -a -n "$dmodel" ]; then + ans=$dmodel +fi +dmodel=$ans +case "$ans" in + 1) + use_color=n; def_color="#undef COLOR" + use_menu=n; def_menu="#undef MENU" + use_mouse=n; def_mouse="#undef MOUSE" + use_cookie=n; def_cookie="#undef USE_COOKIE" + use_ssl=n; def_ssl="#undef USE_SSL" + save_params + customized=y + ;; + 2) + use_color=y; def_color="#define COLOR" + use_menu=y; def_menu="#define MENU" + use_mouse=n; def_mouse="#undef MOUSE" + use_cookie=n; def_cookie="#undef USE_COOKIE" + use_ssl=n; def_ssl="#undef USE_SSL" + save_params + customized=y + ;; + 3) + use_color=y; def_color="#define COLOR" + use_menu=y; def_menu="#define MENU" + use_mouse=y; def_mouse="#define MOUSE" + use_cookie=n; def_cookie="#undef USE_COOKIE" + use_ssl=n; def_ssl="#undef USE_SSL" + save_params + customized=y + ;; + 4) + use_color=y; def_color="#define COLOR" + use_menu=y; def_menu="#define MENU" + use_mouse=y; def_mouse="#define MOUSE" + use_cookie=y; def_cookie="#define USE_COOKIE" + use_ssl=n; def_ssl="#undef USE_SSL" + save_params + customized=y + ;; + 5) + use_color=y; def_color="#define COLOR" + use_menu=y; def_menu="#define MENU" + use_mouse=y; def_mouse="#define MOUSE" + use_cookie=y; def_cookie="#define USE_COOKIE" + use_ssl=y; def_ssl="#define USE_SSL" + find_ssl + save_params + customized=y + ;; + 6) + ;; + *) + echo "Please input 1-6." + Echo "Which? " + continue + ;; +esac +break +done + +if [ "$customized" != y ]; then + +echo "Do you want to use color ESC sequence for kterm/pxvt " +yesno use_color "$use_color" y +echo "use_color=$use_color" >> config.param +if [ "$use_color" = y ]; then + def_color="#define COLOR" +else + def_color="#undef COLOR" +fi + +echo 'Do you want to use mouse? (It requires xterm/kterm)' +yesno use_mouse "$use_mouse" n +echo "use_mouse=$use_mouse" >> config.param +if [ "$use_mouse" = y ]; then + def_mouse="#define MOUSE" +else + def_mouse="#undef MOUSE" +fi + +echo "Do you want to use popup menu?" +yesno use_menu "$use_menu" y +echo "use_menu=$use_menu" >> config.param +if [ "$use_menu" = y ]; then + def_menu="#define MENU" +else + def_menu="#undef MENU" +fi + +#echo "Do you want to use matrix in rendering table?" +#if [ "$use_matrix" = n ]; then +# Echo '[n]? ' +# read ks_ans +# if [ "$ks_ans" = 'y' ]; then +# use_matrix='y' +# fi +#else +# Echo '[y]? ' +# read ks_ans +# if [ "$ks_ans" = 'n' ]; then +# use_matrix='n' +# else +# use_matrix='y' +# fi +#fi +#use_matrix=y +#echo "use_matrix=$use_matrix" >> config.param +#if [ "$use_matrix" = y ]; then +# def_matrix="#define MATRIX 1" +#else +# def_matrix="#undef MATRIX" +#fi + +echo "Do you want to use cookie?" +yesno use_cookie "$use_cookie" n +echo "use_cookie=$use_cookie" >> config.param +if [ "$use_cookie" = y ]; then + def_cookie="#define USE_COOKIE" +else + def_cookie="#undef USE_COOKIE" +fi + +echo "Do you want to use SSL?" +echo '(You need OpenSSL library; Please see http://www.openssl.org/)' +yesno use_ssl "$use_ssl" n +echo "use_ssl=$use_ssl" >> config.param +if [ "$use_ssl" = y ]; then + def_ssl="#define USE_SSL" + find_ssl +else + def_ssl="#undef USE_SSL" + ssllib="" + sslinclude="" +fi + +fi + +if [ "$use_ssl" = y ]; then + echo "Do you want SSL verification support" + echo '(Your SSL library must be version 0.8 or later)' + yesno use_ssl_verify "$use_ssl_verify" n + echo "use_ssl_verify=$use_ssl_verify" >> config.param + if [ "$use_ssl_verify" = y ]; then + def_use_ssl_verify="#define USE_SSL_VERIFY" + else + def_use_ssl_verify="#undef USE_SSL_VERIFY" + fi +else + use_ssl_verify=n + def_use_ssl_verify="#undef USE_SSL_VERIFY" +fi + +if [ -z "$ded" ] ; then ded=`./which \vi` ; fi +if [ -n "`echo $ded | grep 'no'`" ] ; then ded=vi ; fi +echo "Input your favorite editor program." +Echo "(Default: $ded) " +readdir "$ded" +editor=$_dir +echo "ded='$editor'" >> config.param + +if [ -z "$dmail" ] ; then + if ./which \mailx > /dev/null + then + dmail=`./which \mailx` + else + dmail=`./which \mail` + fi + if [ -n "`echo $dmail | grep 'no'`" ] ; then dmail=mailx ; fi +fi +echo "Input your favorite mailer program." +Echo "(Default: $dmail) " +readdir "$dmail" +mailer=$_dir +echo "dmail='$mailer'" >> config.param + + +if [ -z "$dbrowser" ] ; then + if ./which netscape > /dev/null + then + dbrowser=`./which netscape` + elif ./which iexplore > /dev/null + then + dbrowser=`./which iexplore` + else + dbrowser=`./which lynx` + fi + if [ -n "`echo $dbrowser | grep 'no'`" ] ; then dbrowser=netscape ; fi +fi +echo "Input your favorite external browser program." +Echo "(Default: $dbrowser) " +readdir "$dbrowser" +brz=$_dir +echo "dbrowser='$brz'" >> config.param + +if [ -z "$dcc" ] ; then + if ./which gcc >/dev/null + then + dcc=gcc + else + dcc=cc + fi +fi +echo "Input your favorite C-compiler." +Echo "(Default: $dcc) " +readanswer cc "$dcc" +echo "dcc='$cc'" >> config.param + +if [ -z "$dcflags" ] ; then dcflags="-O" ; fi +echo "Input your favorite C flags." +Echo "(Default: $dcflags) " +readanswer cflags "$dcflags" +echo "dcflags='$cflags'" >> config.param + +bsdinclude='' +if [ ! -f /usr/include/netinet/in.h ] ; then + if [ -f /usr/include/bsd/netinet/in.h ] ; then + bsdinclude='-I/usr/include/bsd' + elif [ -f /usr/bsdinclude/netinet/in.h ] ; then + bsdinclude='-I/usr/bsdinclude' + else + echo "It seems you don't have some include files for networking." + fi +fi + +termlib='' +cat > _zmachdep.c << EOF +main() +{ + char bp[100]; + tgetent(bp,getenv("TERM")); +} +EOF + +if [ -z "$dtermlib" ]; then + TERM_LIBS='termcap termlib terminfo mytinfo curses ncurses' + + for lib in $TERM_LIBS + do + for libdir in /lib /usr/lib /usr/local/lib /usr/ucblib /usr/ccslib /usr/ccs/lib + do + if [ -f $libdir/lib$lib.a -o -f $libdir/lib$lib.so ] ; then + # check if the lib works... + Echo "Terminal library -l$lib found at $libdir, " + if $cc $cflags -o _zmachdep _zmachdep.c -l$lib > /dev/null 2>&1 + then + echo "and it seems to work." + termlib=-l$lib + else + echo "but it doesn't seem to work." + fi + fi + done + done + if [ -z "$termlib" ]; then + Echo "termcap/curses library not found; I hope -ltermcap works." + termlib='-ltermcap' + fi + dtermlib=$termlib +fi +echo 'Which terminal library do you want to use? (type "none" if you do not need one)' +Echo "(default: $dtermlib) " +readanswer termlib "$dtermlib" +if [ "$termlib" = none ]; then + termlib="" +else + echo "dtermlib='$termlib'" >> config.param +fi + +## Setup for math library +if [ $sysname = Rhapsody -o $sysname = "Mac OS" ]; then + echo "MacOS X doesn't need -lm." + mathlib="" +else + mathlib="-lm" +fi + +## look for GPM library +use_gpm="" +gpmlib="" +if [ "$use_mouse" = y ]; then + for libdir in /lib /usr/lib /usr/local/lib + do + if [ -f $libdir/libgpm.a -o -f $libdir/libgpm.so ]; then + echo "GPM library found." + use_gpm="#define USE_GPM" + gpmlib="-lgpm" + fi + done +fi + +case $sysname in + freebsd|FreeBSD) + use_sysmouse="#define USE_SYSMOUSE" + ;; + *) + use_sysmouse="#undef USE_SYSMOUSE" + ;; +esac + +extlib='' + +case $sysname in + *bsd) + searchlibs="socket nsl" + ;; + *BSD) + searchlibs="socket nsl" + ;; + *) + searchlibs="bsd BSD 44bsd socket nsl" + ;; +esac +for lib in $searchlibs +do + for libdir in /lib /usr/lib /usr/local/lib /usr/ucblib /usr/ccslib /usr/ccs/lib + do + if [ -f $libdir/lib$lib.a -o -f $libdir/lib$lib.so ] ; then + extlib="$extlib -l$lib" + break + fi + done +done +if [ $sysname = "HP-UX" ]; then + extlib="$extlib -ldld" +fi +if [ -n "$extlib" ]; then + echo "additional library found: $extlib" +fi + +gclib='' +gcinclude='' +gctarget='' +for libdir in /lib /usr/lib /usr/local/lib /usr/ucblib /usr/ccslib /usr/ccs/lib ${HOME}/lib +do + if [ -f $libdir/libgc.a -o -f $libdir/libgc.so ] ; then + echo "$libdir/libgc found" + gclib="-L$libdir -lgc" + break + fi +done +for inc in /usr/include /usr/include/gc /usr/local/include /usr/local/include/gc ${HOME}/include +do + if [ -f $inc/gc.h ]; then + echo "$inc/gc.h found" + gcinclude=$inc + break + fi +done + +case $sysname in + linux|Linux|LINUX|aix|Aix|AIX) + # these OS requires gcmain.c, which include gc/gc_priv.h + # therefore we use gc library comes with w3m + echo "Your OS is $sysname; using gc library comes with w3m." + gcinclude="" + gclib="" + ;; +esac + +if [ -n "$gclib" -a -n "$gcinclude" ]; then + Echo GC library found on your system... + cat > _zmachdep.c << EOF +#include <gc.h> +main() +{ + extern unsigned GC_version; + printf("%d%02d%03d\n",(GC_version>>16)&0xff,(GC_version>>8)&0xff,GC_version&0xff); +} +EOF + if $cc $cflags -I$gcinclude -o _zmachdep _zmachdep.c $gclib > /dev/null 2>&1 + then + echo "and it seems to work." + gcversion=`./_zmachdep` + echo "GC_version is $gcversion." + if [ $gcversion -lt $mygcversion ]; then + echo "GC library on your system seems to be old." + Echo "Do you want to use GC library comes with w3m?[y] " + read ans + if [ "$ans" = 'n' -o "$ans" = 'N' ]; then + cflags="$cflags -I$gcinclude" + else + cflags="$cflags -I./gc/include" + gclib="gc/gc.a" + gctarget=$gclib + fi + else + cflags="$cflags -I$gcinclude" + fi + else + echo "but it doesn't seem to work." + cflags="$cflags -I./gc/include" + gclib="gc/gc.a" + gctarget=$gclib + fi +fi + +if [ -z "$gclib" -o -z "$gcinclude" ]; then + cflags="$cflags -I./gc/include" + gclib="gc/gc.a" + gctarget="$gclib" +fi + +# Apply patch. +if [ "$gclib" = "gc/gc.a" -a ! -f patch_done ]; then + patchfile="" + case "$platform:$sysname" in + [Aa]lpha:Linux) + patchfile=Patches/alpha ;; + [Ss]parc:Linux|sun4*:Linux) + if [ "$sysversion1" = 2 -a "$sysversion2" = 2 ] + then + patchfile=Patches/linux2.2sparc + fi + ;; + ARM*:Linux|arm*:Linux) + patchfile=Patches/armlinux + ;; + [mM]ips*:Linux|MIPS*:Linux) + patchfile=Patches/mipsel + ;; + *:HP-UX) + if [ "$sysversion2" = 11 ] + then + patchfile=Patches/hpux11 + fi + ;; + macppc:NetBSD) + patchfile = Patches/macppc + ;; + R3000:*System_V*|R4000:UNIX_SYSV|R*000:UNIX_SV) + # EWS-4800 + patchfile=Patches/ews4800 + ;; + *:NEWS-OS) + patchfile=Patches/newsos6 + ;; + *:Rhapsody|*:"Mac OS") + # MacOS X + patchfile=Patches/macosx + ;; + *:OS/2) + # OS/2 + patchfile=Patches/os2 + esac + + if [ -n "$patchfile" -a -f "$patchfile" ]; then + patch -p0 < $patchfile + echo "dpatch='$patch'" >> config.param + touch patch_done + fi +fi + + +echo "Input additional LD flags other than listed above, if any:" +if [ -n "$dldflags" ]; then + Echo "(default: $dldflags) : " +else + Echo ": " +fi +readanswer ldflags "$dldflags" ok +if [ -z "$ldflags" ]; then + ldflags=$dldflags +fi +echo "dldflags='$ldflags'" >> config.param + +echo "Checking machine dependency." + +###### mime.types +MIME_TYPES="" +for d in /usr/lib /usr/local/lib /usr/local/lib/mosaic /usr/local/mosaic /usr/local/netscape /usr/local/lib/netscape +do + if [ -f $d/mime.types ]; then + MIME_TYPES="$d/mime.types" + fi +done +if [ -z "$MIME_TYPES" ]; then + echo "Global mime.types not found; Hope /usr/local/lib/mime.types works." + MIME_TYPES=/usr/local/lib/mime.types +fi + +####### ranlib +if ./which ranlib > /dev/null +then + echo "You have ranlib." + ranlib_cmd=ranlib +else + if [ $sysname = "OS/2" ]; then + ranlib_cmd=rem + else + echo "You don't have ranlib." + ranlib_cmd=: + fi +fi + +####### mkdir -p +if mkdir -p hogege +then + echo "You have mkdir -p." + MKDIR="mkdir -p" +else + MKDIR="mkdir" +fi +rm -rf hogege 2>&1 >/dev/null + +####### strcasecmp +cat > _zmachdep.c << EOF +#include <string.h> +main() +{ + int i; + i = strcasecmp("abc","def"); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have strcasecmp()." + strcasecmp_flg="#define STRCASECMP" +else + echo "You don't have strcasecmp()." + strcasecmp_flg="#undef STRCASECMP" +fi + +####### strchr +cat > _zmachdep.c << EOF +#include <string.h> +main() +{ + char *p, *q = "abc"; + p = strchr(q,'c'); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have strchr()." + strchr_flg="#define STRCHR" +else + echo "You don't have strchr()." + strchr_flg="#undef STRCHR" +fi + +####### strerror +cat > _zmachdep.c << EOF +main() +{ + int i; + i = strerror(0); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have strerror()." + strerror_flg="#define STRERROR" +else + echo "You don't have strerror()." + strerror_flg="#undef STRERROR" +fi + + +####### sys_errlist +cat > _zmachdep.c << EOF +main() +{ + extern char sys_errlist[]; +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have sys_errlist[]." + syserrlist_flg="#define SYS_ERRLIST" +else + echo "You don't have sys_errlist[]." + syserrlist_flg="#undef SYS_ERRLIST" +fi + +####### bcopy +cat > _zmachdep.c << EOF +main() +{ + char x[1],y[1]; + bzero(x,1); + bcopy(x,y,1); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have bcopy()." + bcopy_flg="#undef NOBCOPY" +else + echo "You don't have bcopy()." + bcopy_flg="#define NOBCOPY" +fi + +####### waitpid +cat > _zmachdep.c << EOF +#include <sys/types.h> +#include <sys/wait.h> + +main() +{ + pid_t pid; + int status; + if ((pid = fork()) == 0) { + sleep(10); + exit(1); + } + while(waitpid(pid,&status,WNOHANG)); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have waitpid()." + waitpid_flg="#define HAVE_WAITPID" +else + echo "You don't have waitpid()." + waitpid_flg="#undef HAVE_WAITPID" +fi + +####### wait3 +cat > _zmachdep.c << EOF +#include <sys/types.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#ifndef NULL +#define NULL 0 +#endif + +main() +{ + int pid; + int status; + if ((pid = fork()) == 0) { + sleep(10); + exit(1); + } + while(wait3(&status,WNOHANG,NULL) > 0); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have wait3()." + wait3_flg="#define HAVE_WAIT3" +else + echo "You don't have wait3()." + wait3_flg="#undef HAVE_WAIT3" +fi + +####### strftime +cat > _zmachdep.c << EOF +#include <time.h> + +main() +{ + time_t ct; + struct tm *tm; + char t[80]; + time(&ct); + strftime(t, 80, "%a, %d %b %Y %H:%M:%S GMT",gmtime(&ct)); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have strftime()." + strftime_flg="#define HAVE_STRFTIME" +else + echo "You don't have strftime()." + strftime_flg="#undef HAVE_STRFTIME" +fi + + +####### getcwd +cat > _zmachdep.c << EOF +#include <sys/param.h> +#include <unistd.h> +main() +{ + char path[MAXPATHLEN]; + getcwd(path,MAXPATHLEN); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have getcwd()." + getcwd_flg="#define GETCWD" +else + echo "You don't have getcwd()." + getcwd_flg="#undef GETCWD" +fi + +####### getwd +cat > _zmachdep.c << EOF +main() +{ + char path[64]; + getwd(path); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have getwd()." + getwd_flg="#define GETWD" +else + echo "You don't have getwd()." + getwd_flg="#undef GETWD" +fi + +####### readlink +cat > _zmachdep.c << EOF +main() +{ + char path[64],lpath[64]; + readlink(path,lpath,64); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have readlink()." + readlink_flg="#define READLINK" +else + echo "You don't have readlink()." + readlink_flg="#undef READLINK" +fi + +####### setenv +cat > _zmachdep.c << EOF +#include <stdlib.h> +main() +{ + setenv("HOGE","hoge",1); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have setenv()." + setenv_flg="#define HAVE_SETENV" +else + echo "You don't have setenv()." + setenv_flg="#undef HAVE_SETENV" +fi + +####### putenv +cat > _zmachdep.c << EOF +#include <stdlib.h> +main() +{ + putenv("HOGE=hoge"); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have putenv()." + putenv_flg="#define HAVE_PUTENV" +else + echo "You don't have putenv()." + putenv_flg="#undef HAVE_PUTENV" +fi + +####### sigsetjmp +cat > _zmachdep.c << EOF +#include <setjmp.h> +main() +{ + jmp_buf env; + if (sigsetjmp(env,1) != 0) { + exit(0); + } + siglongjmp(env,1); +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "You have sigsetjmp()." + setjmp_def="#define SETJMP(env) sigsetjmp(env,1)" + longjmp_def="#define LONGJMP(env,val) siglongjmp(env,val)" + jmpbuf_def="#define JMP_BUF sigjmp_buf" +else + echo "You don't have sigsetjmp()." + setjmp_def="#define SETJMP(env) setjmp(env)" + longjmp_def="#define LONGJMP(env,val) longjmp(env)" + jmpbuf_def="#define JMP_BUF jmp_buf" +fi + +####### fclose +cat > _zmachdep.c << EOF +#include <stdio.h> +#include <stdlib.h> +main() +{ + void (*c)() = fclose; +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "fclose() is declared." + fclose_dcl='' +else + echo "fclose() is not declared." + fclose_dcl='void fclose(FILE*);' +fi + +####### pclose +cat > _zmachdep.c << EOF +#include <stdio.h> +#include <stdlib.h> +main() +{ + void (*c)() = pclose; +} +EOF +if $cc $cflags -o _zmachdep _zmachdep.c > /dev/null 2>&1 +then + echo "pclose() is declared." + pclose_dcl='' +else + echo "pclose() is not declared." + pclose_dcl='void pclose(FILE*);' +fi + +####### termios/termio/sgtty +term_if='#define SGTTY' +if [ $sysname = "HP-UX" ]; then + echo "Your OS is HP-UX; using termio" + term_if="#define TERMIO" +elif [ $sysname = "CYGWIN" ]; then + echo "Your OS is CYGWIN; using termios" + term_if="#define TERMIOS" +elif [ $sysname = "OS/2" ]; then + echo "Your OS is OS/2; using termios" + term_if='#define TERMIOS' +elif [ -r /usr/include/termios.h ]; then + echo "You have termios." + term_if='#define TERMIOS' +elif [ -r /usr/include/termio.h ]; then + echo "You have termio." + term_if='#define TERMIO' +elif [ -r /usr/include/sgtty.h ]; then + echo "You have sgtty." + term_if='#define SGTTY' +else + echo "Do you have tty interface? I can't find one but I hope sgtty works..." +fi + +####### dirent/direct +dir_if='' +if [ $sysname = "CYGWIN" ]; then + echo "Your OS is CYGWIN; using dirent.h" + dir_if='#define DIRENT' +elif [ $sysname = "OS/2" ]; then + echo "Your OS is OS/2; using dirent.h" + dir_if='#define DIRENT' +elif [ -r /usr/include/dirent.h ]; then + echo "You have dirent.h." + dir_if='#define DIRENT' +elif [ -r /usr/include/sys/dir.h ]; then + echo "You have sys/dir.h." + dir_if='' +else + echo "Do you have directory interface? I can't find one but I hope sys/dir.h works..." +fi + +# check signal handler + +do_sigtest int int +if [ $stat = 0 -a -z "$warning" ] +then + echo 'signal handler is int handler(int).' + sig_type='typedef int MySignalHandler;' + sig_arg='#define SIGNAL_ARG int _dummy' + sig_arglist='#define SIGNAL_ARGLIST 0' + sig_return='#define SIGNAL_RETURN return 0' +else + do_sigtest int void + if [ $stat = 0 -a -z "$warning" ] + then + echo 'signal handler is int handler(void).' + sig_type='typedef int MySignalHandler;' + sig_arg='#define SIGNAL_ARG void' + sig_arglist='#define SIGNAL_ARGLIST' + sig_return='#define SIGNAL_RETURN return 0' + else + do_sigtest void int + if [ $stat = 0 -a -z "$warning" ] + then + echo 'signal handler is void handler(int).' + sig_type='typedef void MySignalHandler;' + sig_arg='#define SIGNAL_ARG int _dummy' + sig_arglist='#define SIGNAL_ARGLIST 0' + sig_return='#define SIGNAL_RETURN return' + else + do_sigtest void void + if [ $stat = 0 -a -z "$warning" ] + then + echo 'signal handler is void handler(void).' + else + echo 'I could not find the type of signal handler. I hope void handler(void) works.' + fi + sig_type='typedef void MySignalHandler;' + sig_arg='#define SIGNAL_ARG void' + sig_arglist='#define SIGNAL_ARGLIST' + sig_return='#define SIGNAL_RETURN return' + fi + fi +fi + +# check for float.h +cat > _zmachdep.c << EOF +#include <float.h> +main() +{ + ; +} +EOF +if $cc $cflags -c _zmachdep.c > /dev/null 2>&1 +then + echo "You have float.h." + no_float_h='#undef NO_FLOAT_H' +else + no_float_h='#define NO_FLOAT_H 1' +fi + +###### IPv6 support check +cat > _zmachdep.c <<EOF +#include <sys/types.h> +#include <sys/socket.h> +main() +{ + if (socket(AF_INET6, SOCK_STREAM, 0) < 0) + exit(1); + else + exit(0); +} +EOF +ipv6="#undef INET6" +v6lib='' +if $cc $cflags -o _zmachdep _zmachdep.c $extlib > /dev/null 2>&1 +then + if ./_zmachdep; then + ipv6="#define INET6" + fi + case $sysname in + *BSD|*bsd) + cat > _zmachdep.c <<EOF +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +struct addrinfo *hints, **res; + +int main() { + getaddrinfo("null", "null", hints, res); +} +EOF + if $cc $cflags -o _zmachdep _zmachdep.c $extlib > /dev/null 2>&1 + then + echo "You have getaddrinfo() in libc." + else + for libdir in /usr/local/v6/lib /usr/local/lib /usr/lib + do + if [ -e $libdir/libinet6.a ]; then + if [ "$libdir" != "/usr/lib" ]; then + v6lib="-L$libdir" + fi + v6lib="$v6lib -linet6" + if $cc $cflags -o _zmachdep _zmachdep.c $extlib $v6lib > /dev/null 2>&1 + then + echo "You have getaddrinfo() in libinet6." + fi + break + fi + done + if [ "X$v6lib" = "X" ]; then + echo "You don't have getaddrinfo()." + ipv6="#undef INET6" + fi + fi + ;; + CYGWIN*) + ipv6="#undef INET6" + ;; + esac +fi +if [ "$ipv6" = "#undef INET6" ]; then + echo "You don't have IPv6 support." +else + echo "You have IPv6 support." +fi + + + +rm -f _zmachdep$extension _zmachdep.c _zmachdep.o +echo "------------ Configuration done ------------" + +# set model name +case $dmodel in + 1) modelname=baby;; + 2) modelname=little;; + 3) modelname=mouse;; + 4) modelname=cookie;; + 5) modelname=monster;; + 6) modelname=custom;; +esac + +cat > extrvers.c << EOF +#include <stdio.h> +#include <string.h> +#include "version.c" +main() +{ + char *p = strchr(version,'/'); + if (p == NULL) + printf("unknown\n"); + else + printf("%s\n",p+1); +} +EOF +$cc $cflags -o extrvers extrvers.c > /dev/null 2>&1 +w3mversion=`./extrvers` +echo "Current w3m version is $w3mversion." +rm -f extrvers.c extrvers$extension + +echo "Extracting config.h" +cat > config.h << END_OF_CONFIG_H +/* + * Configuration for w3m + */ + +#ifndef _CONFIGURED_ +#define _CONFIGURED_ + +/* User Configuration */ + +/* + If you define DICT, you can use dictionary look-up function + in w3m. See README.dict for detail. +*/ +#undef DICT + +/* + If you define USE_MARK, you can use set-mark (C-SPC), + goto-next-mark (ESC p), goto-next-mark (ESC n) and + mark-by-regexp ("). +*/ +#undef USE_MARK + +/* + If you want to load and save URL history. + */ +#define USE_HISTORY + +/* + BG_COLOR enables w3m to set background color. + */ +#define BG_COLOR + +/* + VIEW_UNSEENOBJECTS enables w3m to make a link to unseen objects. + e.g. background image. + */ +#undef VIEW_UNSEENOBJECTS + +/* + VI_PREC_NUM enables vi-like behavior for '2 SPC' or '2 b' + */ +#undef VI_PREC_NUM + +/* + * Do word fill + */ +#undef FORMAT_NICE + +/* + * Support Gopher protocol + */ +#undef USE_GOPHER + +/* + * Support NNTP + */ +$def_use_nntp + +/* + * Support ANSI color escape sequences + */ +$def_ansi_color + +/* + * Enable id attribute + */ +#define ID_EXT + +/* + * Save Current-buffer Information + */ +#define BUFINFO + +/* + * Support EGD (Entropy Gathering Daemon) + */ +#undef USE_EGD + +/* + * MENU_MAP enables w3m to show image map link with popup menu. + */ +#define MENU_MAP + +/* + * Use Emacs-like key binding for file name completion + */ +#undef EMACS_LIKE_LINEEDIT + +/* + * Remove line trailing spaces in html buffer. + */ +#undef ENABLE_REMOVE_TRAILINGSPACES + +/**********************************************************/ +#ifdef makefile_parameter + +BIN_DIR = $bindir +HELP_DIR = $helpdir +LIB_DIR = $suplibdir +HELP_FILE = $helpfile +SYS_LIBRARIES = $gpmlib $extlib $termlib $ssllib $v6lib +LOCAL_LIBRARIES = $ldflags +CC = $cc +MYCFLAGS = $cflags $bsdinclude $sslinclude +GCCFLAGS = $cflags -DATOMIC_UNCOLLECTABLE -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DSILENT -DNO_DEBUGGING #-DNO_SIGNALS +KEYBIND_SRC = $keymap_file.c +KEYBIND_OBJ = $keymap_file.o +EXT=$extension +MATHLIB=$mathlib +GCLIB=$gclib +GCTARGET=$gctarget +RANLIB=$ranlib_cmd +MKDIR=$MKDIR +VERSION=$w3mversion +MODEL=$sysname.$platform-$modelname-$lang +#else +$special_sys + +#define DISPLAY_CODE '$ncode' + +#define JA 0 +#define EN 1 +$use_lang +$def_kanji_symbols +$def_color +$def_mouse +$use_gpm +$use_sysmouse +$def_menu +$def_cookie +$def_ssl +$def_use_ssl_verify +$def_ftppass_hostnamegen +$def_show_params + +#define DEF_EDITOR "$editor" +#define DEF_MAILER "$mailer" +#define DEF_EXT_BROWSER "$brz" + +#define LIB_DIR "$suplibdir" +#define HELP_DIR "$helpdir" +#define HELP_FILE "w3mhelp.html" +#define W3MCONFIG "w3mconfig" + +#define RC_DIR "~/.w3m/" +#define BOOKMARK "bookmark.html" +#define CONFIG_FILE "config" +#define KEYMAP_FILE "keymap" +#define MENU_FILE "menu" +#define COOKIE_FILE "cookie" +#define HISTORY_FILE "history" + +#define USER_MAILCAP RC_DIR "/mailcap" +#define SYS_MAILCAP "/etc/mailcap" +#define USER_MIMETYPES "~/.mime.types" +#define SYS_MIMETYPES "$MIME_TYPES" + +#define DEF_SAVE_FILE "index.html" + +$term_if +$dir_if +$strcasecmp_flg +$strchr_flg +$strerror_flg +$syserrlist_flg +$bcopy_flg +$waitpid_flg +$wait3_flg +$strftime_flg +$getdtablesize_flg +$getcwd_flg +$getwd_flg +$readlink_flg +$setenv_flg +$putenv_flg +$readlink_flg +$fclose_dcl +$pclose_dcl +$setjmp_def +$longjmp_def +$jmpbuf_def + +$sig_type +$sig_arg +$sig_arglist +$sig_return +/* + If you want to use IPv6, define this symbol. + */ +$ipv6 + +#undef TABLE_EXPAND +#undef TABLE_NO_COMPACT +#define NOWRAP 1 +#define NEW_FORM 1 +#define MATRIX 1 +$no_float_h + +#endif /* makefile_parameter */ +#endif /* _CONFIGURED_ */ + +END_OF_CONFIG_H + +echo '' +echo 'config.h is created. See config.h for further configuration.' +echo '' +echo 'Generating dirlist.cgi' + +perl=`./which perl` +if [ `expr "$perl" : 'not found'` != 0 ]; then + perl=/usr/local/bin/perl +fi +if [ $sysname = CYGWIN ]; then + cygwin=1 +else + cygwin=0 +fi +sed -e "s;@PERL@;$perl;" \ + -e "s;@CYGWIN@;$cygwin;" \ + scripts/dirlist.in > scripts/dirlist.cgi + + +echo 'Configuration done. Just type "make".' @@ -0,0 +1,699 @@ +#include <stdio.h> +#include <string.h> +#include "fm.h" + +#ifdef JP_CHARSET +#include "terms.h" +#include "Str.h" + +#ifdef DEBUG +#include <malloc.h> +#endif /* DEBUG */ + +#define uchar unsigned char +#define ushort unsigned short +#define uint unsigned int + +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif +#define TRUE 1 +#define FALSE 0 +#ifdef ESC_CODE +#undef ESC_CODE +#endif +#define ESC_CODE '\033' + +#define CODE_NORMAL 0x00 +#define CODE_OK 0x01 +#define CODE_BROKEN 0x02 +#define CODE_ERROR 0x04 +#define EUC_NOSTATE 0x00 +#define EUC_MBYTE1 0x10 +#define EUC_SS2 0x20 +#define EUC_SS3 0x40 +#define SJIS_NOSTATE 0x00 +#define SJIS_SHIFT_L 0x10 +#define SJIS_SHIFT_H 0x20 +#define ISO_NOSTATE 0x00 +#define ISO_ESC 0x10 +#define ISO_CS94 0x20 +#define ISO_MBCS 0x40 +#define ISO_MBYTE1 0x80 +#define CODE_STATE(c) ((c) & 0x0f) +#define EUC_STATE(c) ((c) & 0xf0) +#define SJIS_STATE(c) ((c) & 0xf0) +#define ISO_STATE(c) ((c) & 0xf0) + +#define CSET_ASCII 0 +#define CSET_X0208 1 +#define CSET_X0201K 2 +#define CSET_UNKNOWN 3 + +#define JSIcode "\033$@" +#define JSOcode "\033(H" +#define J2SIcode "\033$@" +#define J2SOcode "\033(J" +#define NSIcode "\033$B" +#define NSOcode "\033(J" +#define N2SIcode "\033$B" +#define N2SOcode "\033(B" +#define N3SIcode "\033$@" +#define N3SOcode "\033(B" +#define USIcode "\033$" +#define USOcode "\033+" + +static char *SIcode, *SOcode; + +static Str cConvEE(Str is); +static Str cConvEJ(Str is); +static Str cConvES(Str is); +static Str cConvSE(Str is); +static Str cConvJE(Str is); +char checkShiftCode(Str buf, uchar); + +static char *han2zen_tab[] = +{ + "!!", "!#", "!V", "!W", "!\"", "!&", "%r", "%!", + "%#", "%%", "%'", "%)", "%c", "%e", "%g", "%C", + "!<", "%\"", "%$", "%&", "%(", "%*", "%+", "%-", + "%/", "%1", "%3", "%5", "%7", "%9", "%;", "%=", + "%?", "%A", "%D", "%F", "%H", "%J", "%K", "%L", + "%M", "%N", "%O", "%R", "%U", "%X", "%[", "%^", + "%_", "%`", "%a", "%b", "%d", "%f", "%h", "%i", + "%j", "%k", "%l", "%m", "%o", "%s", "!+", "!,", +}; + +typedef struct _ConvRoutine { + char key; + Str(*routine) (); + char *ShiftIn, *ShiftOut; +} ConvRoutine; + +static ConvRoutine FromEJ[] = +{ + {CODE_JIS_J, cConvEJ, JSIcode, JSOcode}, + {CODE_JIS_N, cConvEJ, NSIcode, NSOcode}, + {CODE_JIS_n, cConvEJ, N2SIcode, N2SOcode}, + {CODE_JIS_m, cConvEJ, N3SIcode, N3SOcode}, + {CODE_JIS_j, cConvEJ, J2SIcode, J2SOcode}, + {CODE_SJIS, cConvES, "", ""}, + {CODE_EUC, cConvEE, "", ""}, + {'\0', NULL, NULL, NULL} +}; + +static ConvRoutine ToEJ[] = +{ + {CODE_JIS_J, cConvJE, JSIcode, JSOcode}, + {CODE_JIS_N, cConvJE, NSIcode, NSOcode}, + {CODE_JIS_n, cConvJE, N2SIcode, N2SOcode}, + {CODE_JIS_m, cConvJE, N3SIcode, N3SOcode}, + {CODE_JIS_j, cConvJE, J2SIcode, J2SOcode}, + {CODE_SJIS, cConvSE, "", ""}, + {CODE_EUC, cConvEE, "", ""}, + {'\0', NULL, NULL, NULL} +}; + +char * +GetSICode(char key) +{ + int i; + for (i = 0; FromEJ[i].key != '\0' ; i++) + if (FromEJ[i].key == key) + return FromEJ[i].ShiftIn; + return ""; +} + +char * +GetSOCode(char key) +{ + int i; + for (i = 0; FromEJ[i].key != '\0'; i++) + if (FromEJ[i].key == key) + return FromEJ[i].ShiftOut; + return ""; +} + +static void +n_impr(char s) +{ + fprintf(stderr, "conv: option %c(0x%02x) is not implemented yet... sorry\n", s, s); + exit(1); +} + +Str +conv_str(Str is, char fc, char tc) +{ + int i; + Str os; + static char from_code = '\0'; + static char to_code = '\0'; + static Str (*conv_from) (); + static Str (*conv_to) (); + + if (fc == tc || fc == CODE_ASCII || tc == CODE_ASCII) + return is; + + if (fc == CODE_INNER_EUC) + os = is; + else { + if (from_code != fc) { + for (i = 0; ToEJ[i].key != '\0'; i++) { + if (ToEJ[i].key == fc) { + from_code = fc; + conv_from = *ToEJ[i].routine; + goto next; + } + } + n_impr(fc); + return NULL; + } + next: + os = conv_from(is); + } + if (tc == CODE_INNER_EUC || tc == CODE_EUC) + return os; + else { + if (to_code != tc) { + for (i = 0; FromEJ[i].key != '\0'; i++) { + if (FromEJ[i].key == tc) { + SIcode = FromEJ[i].ShiftIn; + SOcode = FromEJ[i].ShiftOut; + to_code = tc; + conv_to = *FromEJ[i].routine; + goto next2; + } + } + n_impr(tc); + return NULL; + } + next2: + return conv_to(os); + } +} + +Str +conv(char *is, char fc, char tc) +{ + return conv_str(Strnew_charp(is), fc, tc); +} + +static uchar +getSLb(uchar * ptr, uchar * ub) +{ /* Get Shift-JIS Lower byte */ + uchar c = *ptr; + + *ub <<= 1; + if (c < 0x9f) { + if (c > 0x7e) + c--; + *ub -= 1; + c -= 0x3f; + } + else { + c -= 0x9e; + } + return c; +} + +static Str +cConvSE(Str is) +{ /* Convert Shift-JIS to EUC-JP */ + uchar *p, ub, lb; + int state = SJIS_NOSTATE; + Str os = Strnew_size(is->length); + uchar *endp = (uchar *) &is->ptr[is->length]; + + for (p = (uchar *) is->ptr; p < endp; p++) { + switch (state) { + case SJIS_NOSTATE: + if (!(*p & 0x80)) /* ASCII */ + Strcat_char(os, (char) (*p)); + else if (0x81 <= *p && *p <= 0x9f) { /* JIS X 0208, + * 0213 */ + ub = *p & 0x7f; + state = SJIS_SHIFT_L; + } + else if (0xe0 <= *p && *p <= 0xef) { /* JIS X 0208 */ + /* } else if (0xe0 <= *p && *p <= 0xfc) { *//* JIS X 0213 */ + ub = (*p & 0x7f) - 0x40; + state = SJIS_SHIFT_H; + } + else if (0xa0 <= *p && *p <= 0xdf) { /* JIS X 0201-Kana + */ + Strcat_char(os, (char) (han2zen_tab[*p - 0xa0][0] | 0x80)); + Strcat_char(os, (char) (han2zen_tab[*p - 0xa0][1] | 0x80)); + } + break; + case SJIS_SHIFT_L: + case SJIS_SHIFT_H: + if ((0x40 <= *p && *p <= 0x7e) || + (0x80 <= *p && *p <= 0xfc)) { /* JIS X 0208, 0213 */ + lb = getSLb(p, &ub); + ub += 0x20; + lb += 0x20; + Strcat_char(os, (char) (ub | 0x80)); + Strcat_char(os, (char) (lb | 0x80)); + } + else if (!(*p & 0x80)) /* broken ? */ + Strcat_char(os, (char) (*p)); + state = SJIS_NOSTATE; + break; + } + } + return os; +} + +static Str +cConvJE(Str is) +{ /* Convert ISO-2022-JP to EUC-JP */ + uchar *p, ub; + char cset = CSET_ASCII; + int state = ISO_NOSTATE; + Str os = Strnew_size(is->length); + uchar *endp = (uchar *) &is->ptr[is->length]; + + for (p = (uchar *) is->ptr; p < endp; p++) { + switch (state) { + case ISO_NOSTATE: + if (*p == ESC_CODE) /* ESC sequence */ + state = ISO_ESC; + else if (cset == CSET_ASCII || *p < 0x21) + Strcat_char(os, (char) (*p)); + else if (cset == CSET_X0208 && *p <= 0x7e) { + /* JIS X 0208 */ + ub = *p; + state = ISO_MBYTE1; + } + else if (cset == CSET_X0201K && *p <= 0x5f) { + /* JIS X 0201-Kana */ + Strcat_char(os, (char) (han2zen_tab[*p - 0x20][0] | 0x80)); + Strcat_char(os, (char) (han2zen_tab[*p - 0x20][1] | 0x80)); + } + break; + case ISO_MBYTE1: + if (*p == ESC_CODE) /* ESC sequence */ + state = ISO_ESC; + else if (0x21 <= *p && *p <= 0x7e) { /* JIS X 0208 */ + Strcat_char(os, (char) (ub | 0x80)); + Strcat_char(os, (char) (*p | 0x80)); + state = ISO_NOSTATE; + } + else { + Strcat_char(os, (char) (*p)); + state = ISO_NOSTATE; + } + break; + case ISO_ESC: + if (*p == '(') /* ESC ( F */ + state = ISO_CS94; + else if (*p == '$') /* ESC $ F, ESC $ ( F */ + state = ISO_MBCS; + else { + Strcat_char(os, ESC_CODE); + Strcat_char(os, (char) (*p)); + state = ISO_NOSTATE; + } + break; + case ISO_CS94: + if (*p == 'B' || *p == 'J' || *p == 'H') + cset = CSET_ASCII; + else if (*p == 'I') + cset = CSET_X0201K; + else { + Strcat_char(os, ESC_CODE); + Strcat_char(os, '('); + Strcat_char(os, (char) (*p)); + } + state = ISO_NOSTATE; + break; + case ISO_MBCS: + if (*p == '(') { /* ESC $ ( F */ + state = ISO_MBCS | ISO_CS94; + break; + } + case ISO_MBCS | ISO_CS94: + if (*p == 'B' || *p == '@') + cset = CSET_X0208; + else { + Strcat_char(os, ESC_CODE); + Strcat_char(os, '$'); + if (state == (ISO_MBCS | ISO_CS94)) + Strcat_char(os, '('); + Strcat_char(os, (char) (*p)); + } + state = ISO_NOSTATE; + break; + } + } + return os; +} + +static Str +_cConvEE(Str is, char is_euc) +{ /* Convert EUC-JP to EUC-JP / ISO-2022-JP + * (no JIS X 0201-Kana, 0212, 0213-2) */ + uchar *p, ub, euc = 0; + int state = EUC_NOSTATE; + char cset = CSET_ASCII; + Str os; + uchar *endp = (uchar *) &is->ptr[is->length]; + + if (is_euc) { + os = Strnew_size(is->length); + euc = 0x80; + } + else + os = Strnew_size(is->length * 3 / 2); + + for (p = (uchar *) is->ptr; p < endp; p++) { + switch (state) { + case EUC_NOSTATE: + if (!(*p & 0x80)) { /* ASCII */ + if (!is_euc && cset != CSET_ASCII) { + Strcat_charp(os, SOcode); + cset = CSET_ASCII; + } + Strcat_char(os, (char) (*p)); + } + else if (0xa1 <= *p && *p <= 0xfe) { /* JIS X 0208, + * 0213-1 */ + ub = *p; + state = EUC_MBYTE1; + } + else if (*p == EUC_SS2_CODE) /* SS2 + JIS X 0201-Kana */ + state = EUC_SS2; + else if (*p == EUC_SS3_CODE) /* SS3 + JIS X 0212, 0213-2 */ + state = EUC_SS3; + break; + case EUC_MBYTE1: + if (0xa1 <= *p && *p <= 0xfe) { /* JIS X 0208, 0213-1 */ + if (!is_euc && cset != CSET_X0208) { + Strcat_charp(os, SIcode); + cset = CSET_X0208; + } + Strcat_char(os, (char) ((ub & 0x7f) | euc)); + Strcat_char(os, (char) ((*p & 0x7f) | euc)); + } + else if (!(*p & 0x80)) { /* broken ? */ + if (!is_euc && cset != CSET_ASCII) { + Strcat_charp(os, SOcode); + cset = CSET_ASCII; + } + Strcat_char(os, (char) (*p)); + } + state = EUC_NOSTATE; + break; + case EUC_SS2: + if (0xa0 <= *p && *p <= 0xdf) { /* JIS X 0201-Kana */ + if (!is_euc && cset != CSET_X0208) { + Strcat_charp(os, SIcode); + cset = CSET_X0208; + } + Strcat_char(os, (char) (han2zen_tab[*p - 0xa0][0] | euc)); + Strcat_char(os, (char) (han2zen_tab[*p - 0xa0][1] | euc)); + } + state = EUC_NOSTATE; + break; + case EUC_SS3: + state = (EUC_SS3 | EUC_MBYTE1); + break; + case EUC_SS3 | EUC_MBYTE1: + state = EUC_NOSTATE; + break; + } + } + if (!is_euc && cset != CSET_ASCII) + Strcat_charp(os, SOcode); + return os; +} + +static Str +cConvEE(Str is) +{ + return _cConvEE(is, TRUE); +} + +static Str +cConvEJ(Str is) +{ + return _cConvEE(is, FALSE); +} + +void +put_sjis(Str os, uchar ub, uchar lb) +{ + ub -= 0x20; + lb -= 0x20; + if ((ub & 1) == 0) + lb += 94; + ub = ((ub - 1) >> 1) + 0x81; + lb += 0x3f; + if (ub > 0x9f) + ub += 0x40; + if (lb > 0x7e) + lb++; + + Strcat_char(os, (char) (ub)); + Strcat_char(os, (char) (lb)); +} + +static Str +cConvES(Str is) +{ /* Convert EUC-JP to Shift-JIS */ + uchar *p, ub; + int state = EUC_NOSTATE; + Str os = Strnew_size(is->length); + uchar *endp = (uchar *) &is->ptr[is->length]; + + for (p = (uchar *) is->ptr; p < endp; p++) { + switch (state) { + case EUC_NOSTATE: + if (!(*p & 0x80)) /* ASCII */ + Strcat_char(os, (char) (*p)); + else if (0xa1 <= *p && *p <= 0xfe) { /* JIS X 0208, + * 0213-1 */ + ub = *p; + state = EUC_MBYTE1; + } + else if (*p == EUC_SS2_CODE) /* SS2 + JIS X 0201-Kana */ + state = EUC_SS2; + else if (*p == EUC_SS3_CODE) /* SS3 + JIS X 0212, 0213-2 */ + state = EUC_SS3; + break; + case EUC_MBYTE1: + if (0xa1 <= *p && *p <= 0xfe) /* JIS X 0208, 0213-1 */ + put_sjis(os, ub & 0x7f, *p & 0x7f); + else if (!(*p & 0x80)) /* broken ? */ + Strcat_char(os, (char) (*p)); + state = EUC_NOSTATE; + break; + case EUC_SS2: + if (0xa0 <= *p && *p <= 0xdf) /* JIS X 0201-Kana */ + put_sjis(os, han2zen_tab[*p - 0xa0][0], + han2zen_tab[*p - 0xa0][1]); + state = EUC_NOSTATE; + break; + case EUC_SS3: + state = (EUC_SS3 | EUC_MBYTE1); + break; + case EUC_SS3 | EUC_MBYTE1: + state = EUC_NOSTATE; + break; + } + } + return os; +} + +/* + * static ushort sjis_shift[8] = { 0x7fff, 0xffff, 0x0, 0x0, 0x0, + * 0x0, 0xffff, 0x0 }; static ushort sjis_second[16] = { 0x0, 0x0, + * 0x0, 0x0, 0xffff, 0xffff, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, + * 0xffff, 0xffff, 0xffff, 0xffff, 0xfff8 }; */ + +char +checkShiftCode(Str buf, uchar hint) +{ + uchar *p, si = '\0', so = '\0'; + int euc = (CODE_NORMAL | EUC_NOSTATE), + sjis = (CODE_NORMAL | SJIS_NOSTATE), sjis_kana = CODE_NORMAL, + iso = (CODE_NORMAL | ISO_NOSTATE), iso_kana = CODE_NORMAL; + uchar *endp = (uchar *) &buf->ptr[buf->length]; + + if (hint == CODE_INNER_EUC) + return '\0'; + p = (uchar *) buf->ptr; + while (1) { + if (iso != CODE_ERROR && (si == '\0' || so == '\0')) { + switch (ISO_STATE(iso)) { + case ISO_NOSTATE: + if (*p == ESC_CODE) /* ESC sequence */ + iso = (CODE_STATE(iso) | ISO_ESC); + break; + case ISO_ESC: + if (*p == '(') /* ESC ( F */ + iso = (CODE_STATE(iso) | ISO_CS94); + else if (*p == '$') /* ESC $ F, ESC $ ( F */ + iso = (CODE_STATE(iso) | ISO_MBCS); + else + iso = (CODE_STATE(iso) | ISO_NOSTATE); + break; + case ISO_CS94: + if (*p == 'B' || *p == 'J' || *p == 'H') + so = *p; + else if (*p == 'I') + iso_kana = CODE_OK; + iso = (CODE_STATE(iso) | ISO_NOSTATE); + break; + case ISO_MBCS: + if (*p == '(') { /* ESC $ ( F */ + iso = (CODE_STATE(iso) | ISO_MBCS | ISO_CS94); + break; + } + case ISO_MBCS | ISO_CS94: + if (*p == 'B' || *p == '@') + si = *p; + iso = (CODE_STATE(iso) | ISO_NOSTATE); + break; + } + if (*p & 0x80) + iso = CODE_ERROR; + } + if (euc != CODE_ERROR) { + switch (EUC_STATE(euc)) { + case EUC_NOSTATE: + if (!(*p & 0x80)) /* ASCII */ + ; + else if (0xa1 <= *p && *p <= 0xfe) /* JIS X 0208, + * 0213-1 */ + euc = (CODE_STATE(euc) | EUC_MBYTE1); + else if (*p == EUC_SS2_CODE) /* SS2 + JIS X 0201-Kana */ + euc = (CODE_STATE(euc) | EUC_SS2); + else if (*p == EUC_SS3_CODE) /* SS3 + JIS X 0212, 0213-2 */ + euc = (CODE_STATE(euc) | EUC_SS3); + else + euc = CODE_ERROR; + break; + case EUC_MBYTE1: + if (CODE_STATE(euc) == CODE_NORMAL) + euc = CODE_OK; + case EUC_SS3 | EUC_MBYTE1: + if (0xa1 <= *p && *p <= 0xfe) /* JIS X 0208, 0213-1 */ + euc = (CODE_STATE(euc) | EUC_NOSTATE); + else if (euc & CODE_BROKEN) + euc = CODE_ERROR; + else + euc = (CODE_BROKEN | EUC_NOSTATE); + break; + case EUC_SS2: + if (0xa0 <= *p && *p <= 0xdf) /* JIS X 0201-Kana */ + euc = (CODE_STATE(euc) | EUC_NOSTATE); + else + euc = CODE_ERROR; + break; + case EUC_SS3: + if (0xa1 <= *p && *p <= 0xfe) /* JIS X 0212, 0213-2 */ + euc = (CODE_STATE(euc) | EUC_SS3 | EUC_MBYTE1); + else + euc = CODE_ERROR; + break; + } + } + if (sjis != CODE_ERROR) { + switch (SJIS_STATE(sjis)) { + case SJIS_NOSTATE: + if (!(*p & 0x80)) /* ASCII */ + ; + else if (0x81 <= *p && *p <= 0x9f) + sjis = (CODE_STATE(sjis) | SJIS_SHIFT_L); + else if (0xe0 <= *p && *p <= 0xef) /* JIS X 0208 */ + /* else if (0xe0 <= *p && *p <= 0xfc) */ + /* JIS X 0213 */ + sjis = (CODE_STATE(sjis) | SJIS_SHIFT_H); + else if (0xa0 == *p) + sjis = (CODE_BROKEN | SJIS_NOSTATE); + else if (0xa1 <= *p && *p <= 0xdf) /* JIS X 0201-Kana + */ + sjis_kana = CODE_OK; + else + sjis = CODE_ERROR; + break; + case SJIS_SHIFT_L: + case SJIS_SHIFT_H: + if (CODE_STATE(sjis) == CODE_NORMAL) + sjis = CODE_OK; + if ((0x40 <= *p && *p <= 0x7e) || + (0x80 <= *p && *p <= 0xfc)) /* JIS X 0208, + * 0213 */ + sjis = (CODE_STATE(sjis) | SJIS_NOSTATE); + else if (sjis & CODE_BROKEN) + sjis = CODE_ERROR; + else + sjis = (CODE_BROKEN | SJIS_NOSTATE); + break; + } + } + if (euc == CODE_ERROR || sjis == CODE_ERROR) + break; + if (p == endp) + break; + p++; + } + if (iso != CODE_ERROR) { + if (si == '\0' && so == '\0' && iso_kana != CODE_OK) + return '\0'; + switch (si) { + case '@': + switch (so) { + case 'H': + return CODE_JIS_J; + case 'J': + return CODE_JIS_j; + case 'B': + return CODE_JIS_m; + default: + return CODE_JIS_m; + } + case 'B': + switch (so) { + case 'J': + return CODE_JIS_N; + case 'B': + return CODE_JIS_n; + default: + return CODE_JIS_n; + } + default: + switch (so) { + case 'H': + return CODE_JIS_J; + case 'J': + return CODE_JIS_N; + case 'B': + return CODE_JIS_n; + default: + return CODE_JIS_n; + } + } + } + if (hint == CODE_EUC) { + if (euc != CODE_ERROR) + return CODE_EUC; + } else if (hint == CODE_SJIS) { + if (sjis != CODE_ERROR) + return CODE_SJIS; + } + if (CODE_STATE(euc) == CODE_OK) + return CODE_EUC; + if (CODE_STATE(sjis) == CODE_OK) + return CODE_SJIS; + if (CODE_STATE(euc) == CODE_NORMAL) + return CODE_EUC; + if (CODE_STATE(sjis) == CODE_NORMAL) + return CODE_SJIS; + return CODE_EUC; +} +#endif /* JP_CHARSET */ diff --git a/cookie.c b/cookie.c new file mode 100644 index 0000000..43e55cd --- /dev/null +++ b/cookie.c @@ -0,0 +1,697 @@ +/* $Id: cookie.c,v 1.1 2001/11/08 05:14:17 a-ito Exp $ */ + +/* + * References for version 0 cookie: + * [NETACAPE] http://www.netscape.com/newsref/std/cookie_spec.html + * + * References for version 1 cookie: + * [RFC 2109] http://www.ics.uci.edu/pub/ietf/http/rfc2109.txt + * [DRAFT 12] http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt + */ + +#include "fm.h" +#include "html.h" + +#ifdef __EMX__ +#include <strings.h> +#endif + +#ifdef USE_COOKIE +#include <time.h> +#include "local.h" +#include "regex.h" +#include "myctype.h" + +static int is_saved = 1; + +#define contain_no_dots(p, ep) (total_dot_number((p),(ep),1)==0) + +static int +total_dot_number(char *p, char *ep, int max_count) +{ + int count = 0; + if (!ep) + ep = p + strlen(p); + + for (; p < ep && count < max_count; p++) { + if (*p == '.') + count++; + } + return count; +} + + +static char * +domain_match(char *host, char *domain) +{ + int m0, m1; + + /* [RFC 2109] s. 2, "domain-match", case 1 + * (both are IP and identical) + */ + regexCompile("[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*", 0); + m0 = regexMatch(host, 0, 1); + m1 = regexMatch(domain, 0, 1); + if (m0 && m1) { + if (strcasecmp(host, domain) == 0) + return host; + } + else if (!m0 && !m1) { + int offset; + char *domain_p; + /* + * "." match all domains (w3m only), + * and ".local" match local domains ([DRAFT 12] s. 2) + */ + if (strcasecmp(domain, ".") == 0 || + strcasecmp(domain, ".local") == 0) { + offset = strlen(host); + domain_p = &host[offset]; + if (domain[1] == '\0' || contain_no_dots(host, domain_p)) + return domain_p; + } + /* [RFC 2109] s. 2, cases 2, 3 */ + else { + offset = (domain[0] != '.') ? 0 : strlen(host) - strlen(domain); + domain_p = &host[offset]; + if (offset >= 0 && strcasecmp(domain_p, domain) == 0) + return domain_p; + } + } + return NULL; +} + + +static struct portlist * +make_portlist(Str port) +{ + struct portlist *first = NULL, *pl; + char *p; + Str tmp = Strnew(); + + p = port->ptr; + while (*p) { + while (*p && !IS_DIGIT(*p)) + p++; + Strclear(tmp); + while (*p && IS_DIGIT(*p)) + Strcat_char(tmp, *(p++)); + if (tmp->length == 0) + break; + pl = New(struct portlist); + pl->port = atoi(tmp->ptr); + pl->next = first; + first = pl; + } + return first; +} + +static Str +portlist2str(struct portlist *first) +{ + struct portlist *pl; + Str tmp; + + tmp = Sprintf("%d", first->port); + for (pl = first->next; pl; pl = pl->next) + Strcat(tmp, Sprintf(", %d", pl->port)); + return tmp; +} + +static int +port_match(struct portlist *first, int port) +{ + struct portlist *pl; + + for (pl = first; pl; pl = pl->next) { + if (pl->port == port) + return 1; + } + return 0; +} + +static void +check_expired_cookies(void) +{ + struct cookie *p, *p1; + time_t now = time(NULL); + + if (!First_cookie) + return; + + if (First_cookie->expires != (time_t) - 1 && + First_cookie->expires < now) { + if (!(First_cookie->flag & COO_DISCARD)) + is_saved = 0; + First_cookie = First_cookie->next; + } + + for (p = First_cookie; p && p->next; p = p1) { + p1 = p->next; + if (p1->expires != (time_t) - 1 && p1->expires < now) { + if (!(p1->flag & COO_DISCARD)) + is_saved = 0; + p->next = p1->next; + p1 = p; + } + } +} + +static Str +make_cookie(struct cookie *cookie) +{ + Str tmp = Strdup(cookie->name); + Strcat_char(tmp, '='); + Strcat(tmp, cookie->value); + return tmp; +} + +static int +match_cookie(ParsedURL * pu, struct cookie *cookie) +{ + char *domainname = (cookie->version == 0) ? FQDN(pu->host) : pu->host; + + if (!domainname) + return 0; + + if (!domain_match(domainname, cookie->domain->ptr)) + return 0; + if (strncmp(cookie->path->ptr, pu->file, cookie->path->length) != 0) + return 0; +#ifdef USE_SSL + if (cookie->flag & COO_SECURE && pu->scheme != SCM_HTTPS) + return 0; +#else /* not USE_SSL */ + if (cookie->flag & COO_SECURE) + return 0; +#endif /* not USE_SSL */ + if (cookie->portl && !port_match(cookie->portl, pu->port)) + return 0; + + return 1; +} + +struct cookie * +get_cookie_info(Str domain, Str path, Str name) +{ + struct cookie *p; + + for (p = First_cookie; p; p = p->next) { + if (Strcasecmp(p->domain, domain) == 0 && + Strcmp(p->path, path) == 0 && + Strcasecmp(p->name, name) == 0) + return p; + } + return NULL; +} + +Str +find_cookie(ParsedURL * pu) +{ + Str tmp; + struct cookie *p, *p1, *fco = NULL; + int version = 0; + + check_expired_cookies(); + for (p = First_cookie; p; p = p->next) { + if (p->flag & COO_USE && match_cookie(pu, p)) { + for (p1 = fco; p1 && Strcasecmp(p1->name, p->name); p1 = p1->next); + if (p1) + continue; + p1 = New(struct cookie); + bcopy(p, p1, sizeof(struct cookie)); + p1->next = fco; + fco = p1; + if (p1->version > version) + version = p1->version; + } + } + + if (!fco) + return NULL; + + tmp = Strnew(); + if (version > 0) + Strcat(tmp, Sprintf("$Version=\"%d\"; ", version)); + + Strcat(tmp, make_cookie(fco)); + for (p1 = fco->next; p1; p1 = p1->next) { + Strcat_charp(tmp, "; "); + Strcat(tmp, make_cookie(p1)); + if (version > 0) { + if (p1->flag & COO_PATH) + Strcat(tmp, Sprintf("; $Path=\"%s\"", p1->path->ptr)); + if (p1->flag & COO_DOMAIN) + Strcat(tmp, Sprintf("; $Domain=\"%s\"", p1->domain->ptr)); + if (p1->portl) + Strcat(tmp, Sprintf("; $Port=\"%s\"", portlist2str(p1->portl))); + } + } + return tmp; +} + +char *special_domain[] = +{ + ".com", ".edu", ".gov", ".mil", ".net", ".org", ".int", NULL}; + +int +add_cookie(ParsedURL * pu, Str name, Str value, + time_t expires, Str domain, Str path, + int flag, Str comment, int version, + Str port, Str commentURL) +{ + struct cookie *p; + char *domainname = (version == 0) ? FQDN(pu->host) : pu->host; + Str odomain = domain, opath = path; + struct portlist *portlist = NULL; + int use_security = !(flag & COO_OVERRIDE); + +#define COOKIE_ERROR(err) if(!((err) & COO_OVERRIDE_OK) || use_security) return (err) + +#ifdef DEBUG + fprintf(stderr, "host: [%s, %s] %d\n", pu->host, pu->file, flag); + fprintf(stderr, "cookie: [%s=%s]\n", name->ptr, value->ptr); + fprintf(stderr, "expires: [%s]\n", asctime(gmtime(&expires))); + if (domain) + fprintf(stderr, "domain: [%s]\n", domain->ptr); + if (path) + fprintf(stderr, "path: [%s]\n", path->ptr); + fprintf(stderr, "version: [%d]\n", version); + if (port) + fprintf(stderr, "port: [%s]\n", port->ptr); +#endif /* DEBUG */ + /* [RFC 2109] s. 4.3.2 case 2; but this (no request-host) shouldn't happen */ + if (!domainname) + return COO_ENODOT; + + if (domain) { + char *dp; + /* [DRAFT 12] s. 4.2.2 (does not apply in the case that + * host name is the same as domain attribute for version 0 + * cookie) + * I think that this rule has almost the same effect as the + * tail match of [NETSCAPE]. + */ + if (domain->ptr[0] != '.' && + (version > 0 || strcasecmp(domainname, domain->ptr) != 0)) + domain = Sprintf(".%s", domain->ptr); + + if (version == 0) { + /* [NETSCAPE] rule */ + int n = total_dot_number(domain->ptr, + domain->ptr + domain->length, + 3); + if (n < 2) { + COOKIE_ERROR(COO_ESPECIAL); + } else if (n == 2) { + char **sdomain; + int ok = 0; + for (sdomain = special_domain; !ok && *sdomain; sdomain++) { + int offset = domain->length - strlen(*sdomain); + if (offset >= 0 && + strcasecmp(*sdomain, &domain->ptr[offset]) == 0) + ok = 1; + } + if (!ok) + COOKIE_ERROR(COO_ESPECIAL); + } + } else { + /* [DRAFT 12] s. 4.3.2 case 2 */ + if (strcasecmp(domain->ptr, ".local") != 0 && + contain_no_dots(&domain->ptr[1], &domain->ptr[domain->length])) + COOKIE_ERROR(COO_ENODOT); + } + + /* [RFC 2109] s. 4.3.2 case 3 */ + if (!(dp = domain_match(domainname, domain->ptr))) + COOKIE_ERROR(COO_EDOM); + /* [RFC 2409] s. 4.3.2 case 4 */ + /* Invariant: dp contains matched domain */ + if (version > 0 && !contain_no_dots(domainname, dp)) + COOKIE_ERROR(COO_EBADHOST); + } + if (path) { + /* [RFC 2109] s. 4.3.2 case 1 */ + if (version > 0 && strncmp(path->ptr, pu->file, path->length) != 0) + COOKIE_ERROR(COO_EPATH); + } + if (port) { + /* [DRAFT 12] s. 4.3.2 case 5 */ + portlist = make_portlist(port); + if (portlist && !port_match(portlist, pu->port)) + COOKIE_ERROR(COO_EPORT); + } + + if (!domain) + domain = Strnew_charp(domainname); + if (!path) { + path = Strnew_charp(pu->file); + while (path->length > 0 && Strlastchar(path) != '/') + Strshrink(path, 1); + if (Strlastchar(path) == '/') + Strshrink(path, 1); + } + + p = get_cookie_info(domain, path, name); + if (!p) { + p = New(struct cookie); + p->flag = 0; + if (default_use_cookie) + p->flag |= COO_USE; + p->next = First_cookie; + First_cookie = p; + } + + copyParsedURL(&p->url, pu); + p->name = name; + p->value = value; + p->expires = expires; + p->domain = domain; + p->path = path; + p->comment = comment; + p->version = version; + p->portl = portlist; + p->commentURL = commentURL; + + if (flag & COO_SECURE) + p->flag |= COO_SECURE; + else + p->flag &= ~COO_SECURE; + if (odomain) + p->flag |= COO_DOMAIN; + else + p->flag &= ~COO_DOMAIN; + if (opath) + p->flag |= COO_PATH; + else + p->flag &= ~COO_PATH; + if (flag & COO_DISCARD || p->expires == (time_t) - 1) { + p->flag |= COO_DISCARD; + } + else { + p->flag &= ~COO_DISCARD; + is_saved = 0; + } + + check_expired_cookies(); + return 0; +} + +struct cookie * +nth_cookie(int n) +{ + struct cookie *p; + int i; + for (p = First_cookie, i = 0; p; p = p->next, i++) { + if (i == n) + return p; + } + return NULL; +} + +#define str2charp(str) ((str)? (str)->ptr : "") + +void +save_cookies(void) +{ + struct cookie *p; + char *cookie_file; + FILE *fp; + + check_expired_cookies(); + + if (!First_cookie || is_saved || rc_dir_is_tmp) + return; + + cookie_file = rcFile(COOKIE_FILE); + if (!(fp = fopen(cookie_file, "w"))) + return; + + for (p = First_cookie; p; p = p->next) { + if (!(p->flag & COO_USE) || p->flag & COO_DISCARD) + continue; + fprintf(fp, "%s\t%s\t%s\t%ld\t%s\t%s\t%d\t%d\t%s\t%s\t%s\n", + parsedURL2Str(&p->url)->ptr, + p->name->ptr, p->value->ptr, p->expires, + p->domain->ptr, p->path->ptr, p->flag, + p->version, str2charp(p->comment), + (p->portl) ? portlist2str(p->portl)->ptr : "", + str2charp(p->commentURL)); + } + fclose(fp); + chmod(cookie_file, S_IRUSR | S_IWUSR); +} + +static Str +readcol(char **p) +{ + Str tmp = Strnew(); + while (**p && **p != '\n' && **p != '\r' && **p != '\t') + Strcat_char(tmp, *((*p)++)); + if (**p == '\t') + (*p)++; + return tmp; +} + +void +load_cookies(void) +{ + struct cookie *cookie, *p; + FILE *fp; + Str line; + char *str; + + if (!(fp = fopen(rcFile(COOKIE_FILE), "r"))) + return; + + if (First_cookie) { + for (p = First_cookie; p->next; p = p->next); + } + else { + p = NULL; + } + for (;;) { + line = Strfgets(fp); + + if (line->length == 0) + break; + str = line->ptr; + cookie = New(struct cookie); + cookie->next = NULL; + cookie->flag = 0; + cookie->version = 0; + cookie->expires = (time_t) - 1; + cookie->comment = NULL; + cookie->portl = NULL; + cookie->commentURL = NULL; + parseURL(readcol(&str)->ptr, &cookie->url, NULL); + if (!*str) + return; + cookie->name = readcol(&str); + if (!*str) + return; + cookie->value = readcol(&str); + if (!*str) + return; + cookie->expires = (time_t) atol(readcol(&str)->ptr); + if (!*str) + return; + cookie->domain = readcol(&str); + if (!*str) + return; + cookie->path = readcol(&str); + if (!*str) + return; + cookie->flag = atoi(readcol(&str)->ptr); + if (!*str) + return; + cookie->version = atoi(readcol(&str)->ptr); + if (!*str) + return; + cookie->comment = readcol(&str); + if (cookie->comment->length == 0) + cookie->comment = NULL; + if (!*str) + return; + cookie->portl = make_portlist(readcol(&str)); + if (!*str) + return; + cookie->commentURL = readcol(&str); + if (cookie->commentURL->length == 0) + cookie->commentURL = NULL; + + if (p) + p->next = cookie; + else + First_cookie = cookie; + p = cookie; + } + + fclose(fp); +} + +void +initCookie(void) +{ + load_cookies(); + check_expired_cookies(); +} + +Buffer * +cookie_list_panel(void) +{ + Str src = Strnew_charp("<html><head><title>Cookies</title></head>" + "<body><center><b>Cookies</b></center>" + "<p><form method=internal action=cookie>"); + struct cookie *p; + int i; + char *tmp, tmp2[80]; + + if (!use_cookie || !First_cookie) + return NULL; + + Strcat_charp(src, "<ol>"); + for (p = First_cookie, i = 0; p; p = p->next, i++) { + tmp = htmlquote_str(parsedURL2Str(&p->url)->ptr); + if (p->expires != (time_t) - 1) { +#ifdef HAVE_STRFTIME + strftime(tmp2, 80, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&p->expires)); +#else /* not HAVE_STRFTIME */ + struct tm *gmt; + static char *dow[] = { + "Sun ", "Mon ", "Tue ", "Wed ", "Thu ", "Fri ", "Sat " + }; + static char *month[] = { + "Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", + "Jul ", "Aug ", "Sep ", "Oct ", "Nov ", "Dec " + }; + gmt = gmtime( &p->expires ); + strcpy(tmp2,dow[gmt->tm_wday]); + sprintf( &tmp2[4], "%02d ", gmt->tm_mday ); + strcpy(&tmp2[7],month[gmt->tm_mon]); + if( gmt->tm_year < 1900 ) + sprintf( &tmp2[11], "%04d %02d:%02d:%02d GMT", + (gmt->tm_year) + 1900, gmt->tm_hour, gmt->tm_min, + gmt->tm_sec ); + else + sprintf( &tmp2[11], "%04d %02d:%02d:%02d GMT", + gmt->tm_year, gmt->tm_hour, gmt->tm_min, gmt->tm_sec ); +#endif /* not HAVE_STRFTIME */ + } + else + tmp2[0] = '\0'; + Strcat_charp(src, "<li>"); + Strcat_charp(src, "<h1><a href=\""); + Strcat_charp(src, tmp); + Strcat_charp(src, "\">"); + Strcat_charp(src, tmp); + Strcat_charp(src, "</a></h1>"); + + Strcat_charp(src, "<table cellpadding=0>"); + if (!(p->flag & COO_SECURE)) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Cookie:</b></td><td>"); + Strcat_charp(src, htmlquote_str(make_cookie(p)->ptr)); + Strcat_charp(src, "</td></tr>"); + } + if (p->comment) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Comment:</b></td><td>"); + Strcat_charp(src, htmlquote_str(p->comment->ptr)); + Strcat_charp(src, "</td></tr>"); + } + if (p->commentURL) { + Strcat_charp(src, "<tr><td width=\"80\"><b>CommentURL:</b></td><td>"); + Strcat_charp(src, "<a href=\""); + Strcat_charp(src, htmlquote_str(p->commentURL->ptr)); + Strcat_charp(src, "\">"); + Strcat_charp(src, htmlquote_str(p->commentURL->ptr)); + Strcat_charp(src, "</a>"); + Strcat_charp(src, "</td></tr>"); + } + if (tmp2[0]) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Expires:</b></td><td>"); + Strcat_charp(src, tmp2); + if (p->flag & COO_DISCARD) + Strcat_charp(src, " (Discard)"); + Strcat_charp(src, "</td></tr>"); + } + Strcat_charp(src, "<tr><td width=\"80\"><b>Version:</b></td><td>"); + Strcat_charp(src, Sprintf("%d", p->version)->ptr); + Strcat_charp(src, "</td></tr><tr><td>"); + if (p->domain) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Domain:</b></td><td>"); + Strcat_charp(src, htmlquote_str(p->domain->ptr)); + Strcat_charp(src, "</td></tr>"); + } + if (p->path) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Path:</b></td><td>"); + Strcat_charp(src, htmlquote_str(p->path->ptr)); + Strcat_charp(src, "</td></tr>"); + } + if (p->portl) { + Strcat_charp(src, "<tr><td width=\"80\"><b>Port:</b></td><td>"); + Strcat_charp(src, htmlquote_str(portlist2str(p->portl)->ptr)); + Strcat_charp(src, "</td></tr>"); + } + Strcat_charp(src, "<tr><td width=\"80\"><b>Secure:</b></td><td>"); + Strcat_charp(src, (p->flag & COO_SECURE) ? "Yes" : "No"); + Strcat_charp(src, "</td></tr><tr><td>"); + + Strcat(src, Sprintf("<tr><td width=\"80\"><b>Use:</b></td><td>" + "<input type=radio name=\"%d\" value=1%s>Yes" + " " + "<input type=radio name=\"%d\" value=0%s>No", + i, (p->flag & COO_USE) ? " checked" : "", + i, (!(p->flag & COO_USE)) ? " checked" : "")); + Strcat_charp(src, "</td></tr><tr><td><input type=submit value=\"OK\"></table><p>"); + } + Strcat_charp(src, "</ol></form></body></html>"); + return loadHTMLString(src); +} + +void +set_cookie_flag(struct parsed_tagarg *arg) +{ + int n, v; + struct cookie *p; + + while (arg) { + if (arg->arg && *arg->arg && arg->value && *arg->value) { + n = atoi(arg->arg); + v = atoi(arg->value); + if ((p = nth_cookie(n)) != NULL) { + if (v && !(p->flag & COO_USE)) + p->flag |= COO_USE; + else if (!v && p->flag & COO_USE) + p->flag &= ~COO_USE; + if (!(p->flag & COO_DISCARD)) + is_saved = 0; + } + } + arg = arg->next; + } + backBf(); +} + +int +check_cookie_accept_domain(char *domain) +{ + TextListItem *tl; + + if (domain == NULL) + return 0; + + if (Cookie_accept_domains && Cookie_accept_domains->nitem > 0) { + for (tl = Cookie_accept_domains->first; tl != NULL; tl = tl->next) { + if (domain_match(domain, tl->ptr)) + return 1; + } + } + if (Cookie_reject_domains && Cookie_reject_domains->nitem > 0) { + for (tl = Cookie_reject_domains->first; tl != NULL; tl = tl->next) { + if (domain_match(domain, tl->ptr)) + return 0; + } + } + return 1; +} +#endif /* USE_COOKIE */ diff --git a/ctrlcode.h b/ctrlcode.h new file mode 100644 index 0000000..49498b2 --- /dev/null +++ b/ctrlcode.h @@ -0,0 +1,153 @@ + +/* control characters */ + +#define CTRL_A 1 +#define CTRL_B 2 +#define CTRL_C 3 +#define CTRL_D 4 +#define CTRL_E 5 +#define CTRL_F 6 +#define CTRL_G 7 +#define CTRL_H 8 +#define CTRL_I 9 +#define CTRL_J 10 +#define CTRL_K 11 +#define CTRL_L 12 +#define CTRL_M 13 +#define CTRL_N 14 +#define CTRL_O 15 +#define CTRL_P 16 +#define CTRL_Q 17 +#define CTRL_R 18 +#define CTRL_S 19 +#define CTRL_T 20 +#define CTRL_U 21 +#define CTRL_V 22 +#define CTRL_W 23 +#define CTRL_X 24 +#define CTRL_Y 25 +#define CTRL_Z 26 +#define ESC_CODE 27 +#define DEL_CODE 127 + +/* ISO-8859-1 alphabet characters */ + +#define NBSP_CODE 160 +#define IEXCL_CODE 161 +#define CENT_CODE 162 +#define POUND_CODE 163 +#define CURREN_CODE 164 +#define YEN_CODE 165 +#define BRVBAR_CODE 166 +#define SECT_CODE 167 +#define UML_CODE 168 +#define COPY_CODE 169 +#define ORDF_CODE 170 +#define LAQUO_CODE 171 +#define NOT_CODE 172 +#define SHY_CODE 173 +#define REG_CODE 174 +#define MACR_CODE 175 +#define DEG_CODE 176 +#define PLUSMN_CODE 177 +#define SUP2_CODE 178 +#define SUP3_CODE 179 +#define ACUTE_CODE 180 +#define MICRO_CODE 181 +#define PARA_CODE 182 +#define MIDDOT_CODE 183 +#define CEDIL_CODE 184 +#define SUP1_CODE 185 +#define ORDM_CODE 186 +#define RAQUO_CODE 187 +#define FRAC14_CODE 188 +#define FRAC12_CODE 189 +#define FRAC34_CODE 190 +#define IQUEST_CODE 191 +#define AGRAVE_CODE 192 +#define AACUTE_CODE 193 +#define ACIRC_CODE 194 +#define ATILDE_CODE 195 +#define AUML_CODE 196 +#define ARING_CODE 197 +#define AELIG_CODE 198 +#define CCEDIL_CODE 199 +#define EGRAVE_CODE 200 +#define EACUTE_CODE 201 +#define ECIRC_CODE 202 +#define EUML_CODE 203 +#define IGRAVE_CODE 204 +#define IACUTE_CODE 205 +#define ICIRC_CODE 206 +#define IUML_CODE 207 +#define ETH_CODE 208 +#define NTILDE_CODE 209 +#define OGRAVE_CODE 210 +#define OACUTE_CODE 211 +#define OCIRC_CODE 212 +#define OTILDE_CODE 213 +#define OUML_CODE 214 +#define TIMES_CODE 215 +#define OSLASH_CODE 216 +#define UGRAVE_CODE 217 +#define UACUTE_CODE 218 +#define UCIRC_CODE 219 +#define UUML_CODE 220 +#define YACUTE_CODE 221 +#define THORN_CODE 222 +#define SZLIG_CODE 223 +#define aGRAVE_CODE 224 +#define aACUTE_CODE 225 +#define aCIRC_CODE 226 +#define aTILDE_CODE 227 +#define aUML_CODE 228 +#define aRING_CODE 229 +#define aELIG_CODE 230 +#define cCEDIL_CODE 231 +#define eGRAVE_CODE 232 +#define eACUTE_CODE 233 +#define eCIRC_CODE 234 +#define eUML_CODE 235 +#define iGRAVE_CODE 236 +#define iACUTE_CODE 237 +#define iCIRC_CODE 238 +#define iUML_CODE 239 +#define eth_CODE 240 +#define nTILDE_CODE 241 +#define oGRAVE_CODE 242 +#define oACUTE_CODE 243 +#define oCIRC_CODE 244 +#define oTILDE_CODE 245 +#define oUML_CODE 246 +#define DIVIDE_CODE 247 +#define oSLASH_CODE 248 +#define uGRAVE_CODE 249 +#define uACUTE_CODE 250 +#define uCIRC_CODE 251 +#define uUML_CODE 252 +#define yACUTE_CODE 253 +#define thorn_CODE 254 +#define yUML_CODE 255 + +/* EUC control characters */ + +#define EUC_SS2_CODE 0x8e +#define EUC_SS3_CODE 0x8f + +/* internally used characters */ + +/* 0x80-0x8F: use for rule */ + +#define ANSP_CODE 0x90 /* use for empty anchor */ +#define IMSP_CODE 0x91 /* blank around image */ + +#define NBSP "\xa0" +#define ANSP "\x90" +#define IMSP "\x91" + +#include "myctype.h" + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ @@ -0,0 +1,137 @@ + +/* + * From g96p0935@mse.waseda.ac.jp Mon Jun 14 09:34:15 1999 Received: from + * ei5sun.yz.yamagata-u.ac.jp (ei5sun.yz.yamagata-u.ac.jp [133.24.114.42]) + * by ei5nazha.yz.yamagata-u.ac.jp (8.9.3/8.9.3) with ESMTP id JAA20673 for + * <aito@ei5nazha.yz.yamagata-u.ac.jp>; Mon, 14 Jun 1999 09:34:14 +0900 + * (JST) Received: from pandora.mse.waseda.ac.jp + * (root@pandora.mse.waseda.ac.jp [133.9.5.9]) by + * ei5sun.yz.yamagata-u.ac.jp (8.8.0/3.5Wbeta) with ESMTP id JAA23968 for + * <aito@ei5sun.yz.yamagata-u.ac.jp>; Mon, 14 Jun 1999 09:35:30 +0900 (JST) + * Received: from localhost (root@[133.9.85.55]) by pandora.mse.waseda.ac.jp + * (8.9.1+3.0W/3.7W) with ESMTP id JAA18473; Mon, 14 Jun 1999 09:30:31 +0900 + * (JST) Message-Id: <199906140030.JAA18473@pandora.mse.waseda.ac.jp> To: + * aito@ei5sun.yz.yamagata-u.ac.jp Subject: w3m:$B1QOB<-E58!:w5!G=Ec:\(B + * Cc: g96p0935@mse.waseda.ac.jp From: Takashi Nishimoto + * <g96p0935@mse.waseda.ac.jp> X-Mailer: Mew version 1.93 on Emacs 19.34 / + * Mule 2.3 (SUETSUMUHANA) Mime-Version: 1.0 Content-Type: Text/Plain; + * charset=iso-2022-jp Content-Transfer-Encoding: 7bit Date: Mon, 14 Jun + * 1999 09:29:56 +0900 X-Dispatcher: imput version 980506 Lines: 150 + * + * $B@>K\(B@$BAaBg$G$9!#(B + * + * Quick Hack $B$G(B w3m + * $B$K1QOB<-E58!:w5!G=$HC18lC10L$N%+!<%=%k0\F0$r<BAu$7$^(B $B$7$?!#(B + * + * Unix $B$r;H$C$F$$$k$H!"1QJ8$rFI$`5!2q$,B?$$$G$9$M!#(B Emacs + * $BFb$G$O%o%s%?%C%A$G1QOB<-E5$r8!:w$9$k(B sdic + * $B$N$h$&$J%D!<%k$,$"$j$^(B + * $B$9$,!"$A$g$C$H$7$?J8=q$rFI$`$@$1$K$$$A$$$A(B Emacs + * $B$KFI$_9~$`$N$O$+$C$?(B $B$k$$$N$G!"$J$s$H$+(B w3m + * $B$G$G$-$J$$$+$H;W$$!":n6H$KF'$_@Z$j$^$7$?!#(B + * + * $B$9$k$H0U30$K4JC1$K<BAu$G$-$^$7$?!#KM$O(B C + * $B%W%m%0%i%`$N2~B$$O=i$a$F$G$9(B $B$,!"(B Emacs Lisp + * $BJB$N<j7Z$5$G<BAu$G$-$?$3$H$K$O46F0$7$^$7$?!#(B + * + * dictword $B$,D4$Y$kC18l$rJ9$$$F8!:w$9$k4X?t$G!"(B dictwordat + * $B$,%+!<%=%k0L(B + * $BCV$NC18l$r8!:w$9$k4X?t$G$9!#%=!<%9$r8+$l$PL@$i$+$J$h$&$K8!:w$9$k30It%3(B + * $B%^%s%I$O(B w3mdict $B$G$9!#(B Unix + * $B$J$N$G!"IaCJ;H$C$F$$$k%3%^%s%I$X$N(B symlink $B$K$7$F$$$^$9!#(Bw + * $B$K(B dictword$B!"(B W $B$K(B dictwordat $B$r3d$jEv$F$F$$$^(B + * $B$9!#$^$?!"1&<j$GFI$a$k$h$&$K(B ; $B$K$b(B dictwordat + * $B$r3d$jEv$F$F$$$^$9!#(B */ +#include "fm.h" +#include <signal.h> + +#ifdef DICT + +#define DICTCMD "w3mdict " +#define DICTBUFFERNAME "*dictionary*" +/* char *DICTBUFFERNAME="*dictionary*"; */ + +char * +GetWord(char *word) +{ + Line *l = Currentbuf->currentLine; + char *lb = l->lineBuf; + int i, b, e, pos = Currentbuf->pos; + + i = pos; + while (!IS_ALPHA(lb[i]) && i >= 0) + i--; + pos = i; + while (IS_ALPHA(lb[i]) && i >= 0) + i--; + i++; + if (!IS_ALPHA(lb[i])) + return NULL; + b = i; + i = pos; + while (IS_ALPHA(lb[i]) && i <= l->len - 1) + i++; + e = i - 1; + strncpy(word, &lb[b], e - b + 1); + word[e - b + 1] = '\0'; + return word; +} + +void +execdict(char *word) +{ + Buffer *buf; + static char cmd[100], bufname[100]; + MySignalHandler(*prevtrap) (); + + if (word == NULL) + return; + strcpy(cmd, DICTCMD); + strcat(cmd, word); + buf = namedBuffer(Firstbuf, SHELLBUFFERNAME); + if (buf != NULL) + Firstbuf = deleteBuffer(Firstbuf, buf); + + if (cmd == NULL || *cmd == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + prevtrap = signal(SIGINT, intTrap); + crmode(); + buf = getshell(cmd); +/* sprintf(bufname,"*dictionary(%s)*",word); */ +/* buf->buffername = bufname; */ + buf->buffername = DICTBUFFERNAME; + buf->filename = word; + signal(SIGINT, prevtrap); + term_raw(); + if (buf == NULL) { + disp_message("Execution failed", FALSE); + } + else if (buf->firstLine == NULL) { + /* if the dictionary doesn't describe the word. */ + char msg[100]; + sprintf(msg, "Word \"%s\" Not Found", word); + disp_message(msg, FALSE); + + } + else { + buf->nextBuffer = Firstbuf; + Currentbuf = Firstbuf = buf; + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +dictword(void) +{ + execdict(inputStr("(dictionary)!", "")); +} + +void +dictwordat(void) +{ + static char word[100]; + execdict(GetWord(word)); +} +#endif /* DICT */ diff --git a/display.c b/display.c new file mode 100644 index 0000000..80c9a3f --- /dev/null +++ b/display.c @@ -0,0 +1,1064 @@ +/* $Id: display.c,v 1.1 2001/11/08 05:14:32 a-ito Exp $ */ +#include <signal.h> +#include "fm.h" + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#ifdef COLOR + +#define EFFECT_ANCHOR_START effect_anchor_start() +#define EFFECT_ANCHOR_END effect_anchor_end() +#define EFFECT_IMAGE_START effect_image_start() +#define EFFECT_IMAGE_END effect_image_end() +#define EFFECT_FORM_START effect_form_start() +#define EFFECT_FORM_END effect_form_end() +#define EFFECT_ACTIVE_START effect_active_start() +#define EFFECT_ACTIVE_END effect_active_end() +#define EFFECT_VISITED_START effect_visited_start() +#define EFFECT_VISITED_END effect_visited_end() + +/* color: * 0 black * 1 red * 2 green * 3 yellow + * * 4 blue * 5 magenta * 6 cyan * 7 white */ + +#define EFFECT_ANCHOR_START_C setfcolor(anchor_color) +#define EFFECT_IMAGE_START_C setfcolor(image_color) +#define EFFECT_FORM_START_C setfcolor(form_color) +#define EFFECT_ACTIVE_START_C (setfcolor(active_color), underline()) +#define EFFECT_VISITED_START_C setfcolor(visited_color) + +#define EFFECT_IMAGE_END_C setfcolor(basic_color) +#define EFFECT_ANCHOR_END_C setfcolor(basic_color) +#define EFFECT_FORM_END_C setfcolor(basic_color) +#define EFFECT_ACTIVE_END_C (setfcolor(basic_color), underlineend()) +#define EFFECT_VISITED_END_C setfcolor(basic_color) + +#define EFFECT_ANCHOR_START_M underline() +#define EFFECT_ANCHOR_END_M underlineend() +#define EFFECT_IMAGE_START_M standout() +#define EFFECT_IMAGE_END_M standend() +#define EFFECT_FORM_START_M standout() +#define EFFECT_FORM_END_M standend() +#define EFFECT_ACTIVE_START_NC underline() +#define EFFECT_ACTIVE_END_NC underlineend() +#define EFFECT_ACTIVE_START_M bold() +#define EFFECT_ACTIVE_END_M boldend() +#define EFFECT_VISITED_START_M /**/ +#define EFFECT_VISITED_END_M /**/ + +#define define_effect(name_start,name_end,color_start,color_end,mono_start,mono_end) \ +static void name_start { if (useColor) { color_start; } else { mono_start; }}\ +static void name_end { if (useColor) { color_end; } else { mono_end; }} + +define_effect(EFFECT_ANCHOR_START, EFFECT_ANCHOR_END, EFFECT_ANCHOR_START_C, EFFECT_ANCHOR_END_C, EFFECT_ANCHOR_START_M, EFFECT_ANCHOR_END_M) +define_effect(EFFECT_IMAGE_START, EFFECT_IMAGE_END, EFFECT_IMAGE_START_C, EFFECT_IMAGE_END_C, EFFECT_IMAGE_START_M, EFFECT_IMAGE_END_M) +define_effect(EFFECT_FORM_START, EFFECT_FORM_END, EFFECT_FORM_START_C, EFFECT_FORM_END_C, EFFECT_FORM_START_M, EFFECT_FORM_END_M) +static void EFFECT_ACTIVE_START +{ + if (useColor) { + if (useActiveColor) { +#ifdef __EMX__ + if(!getenv("WINDOWID")) + setfcolor(active_color); + else +#endif + { + EFFECT_ACTIVE_START_C; + } + } else { + EFFECT_ACTIVE_START_NC; + } + } else { + EFFECT_ACTIVE_START_M; + } +} + +static void EFFECT_ACTIVE_END +{ + if (useColor) { + if (useActiveColor) { + EFFECT_ACTIVE_END_C; + } else { + EFFECT_ACTIVE_END_NC; + } + } else { + EFFECT_ACTIVE_END_M; + } +} + +static void EFFECT_VISITED_START +{ + if (useVisitedColor) { + if (useColor) { + EFFECT_VISITED_START_C; + } else { + EFFECT_VISITED_START_M; + } + } +} + +static void EFFECT_VISITED_END +{ + if (useVisitedColor) { + if (useColor) { + EFFECT_VISITED_END_C; + } else { + EFFECT_VISITED_END_M; + } + } +} + +#else /* not COLOR */ + +#define EFFECT_ANCHOR_START underline() +#define EFFECT_ANCHOR_END underlineend() +#define EFFECT_IMAGE_START standout() +#define EFFECT_IMAGE_END standend() +#define EFFECT_FORM_START standout() +#define EFFECT_FORM_END standend() +#define EFFECT_ACTIVE_START bold() +#define EFFECT_ACTIVE_END boldend() +#define EFFECT_VISITED_START /**/ +#define EFFECT_VISITED_END /**/ + +#endif /* not COLOR */ + + +#ifndef KANJI_SYMBOLS +static char g_rule[] = "ntwluxkavmqajaaa"; +#endif /* not KANJI_SYMBOLS */ + +/* + * Terminate routine. + */ + +void +fmTerm(void) +{ + if (fmInitialized) { + move(LASTLINE, 0); + clrtoeolx(); + refresh(); +#ifdef MOUSE + if (use_mouse) + mouse_end(); +#endif /* MOUSE */ + reset_tty(); + fmInitialized = FALSE; + } +} + +void +deleteFiles() +{ + Buffer *buf; + char *f; + while (Firstbuf && Firstbuf != NO_BUFFER) { + buf = Firstbuf->nextBuffer; + discardBuffer(Firstbuf); + Firstbuf = buf; + } + while ((f = popText(fileToDelete)) != NULL) + unlink(f); +} + + +/* + * Initialize routine. + */ +void +fmInit(void) +{ + if (!fmInitialized) { + initscr(); + term_raw(); + term_noecho(); +#ifdef MOUSE + if (use_mouse) + mouse_init(); +#endif /* MOUSE */ + } + fmInitialized = TRUE; +} + +/* + * Display some lines. + */ +static Line *cline = NULL; +static int ccolumn = -1; + +static int ulmode = 0, somode = 0, bomode = 0; +static int anch_mode = 0, emph_mode = 0, imag_mode = 0, form_mode = 0, + active_mode = 0, visited_mode = 0; +#ifndef KANJI_SYMBOLS +static int graph_mode = 0; +#endif /* not KANJI_SYMBOLS */ +#ifdef ANSI_COLOR +static Linecolor color_mode = 0; +#endif + +#ifdef BUFINFO +static Buffer *save_current_buf = NULL; +#endif + +static int in_check_url = FALSE; + +void +displayBuffer(Buffer * buf, int mode) +{ + Str msg; + Anchor *aa = NULL; + + if (in_check_url) + return; + if (buf->topLine == NULL && readBufferCache(buf) == 0) { /* clear_buffer */ + mode = B_FORCE_REDRAW; + } + + if (buf->width == 0) + buf->width = COLS; + if (buf->height == 0) + buf->height = LASTLINE + 1; + if (buf->width != INIT_BUFFER_WIDTH && buf->type && !strcmp(buf->type, "text/html")) { + in_check_url = TRUE; + reshapeBuffer(buf); + in_check_url = FALSE; + } + if (mode == B_FORCE_REDRAW || + mode == B_SCROLL || + cline != buf->topLine || + ccolumn != buf->currentColumn) { + if (mode == B_SCROLL && cline && buf->currentColumn == ccolumn) { + int n = buf->topLine->linenumber - cline->linenumber; + if (n > 0 && n < LASTLINE) { + move(LASTLINE, 0); + clrtoeolx(); + refresh(); + scroll(n); + } + else if (n < 0 && n > -LASTLINE) { + rscroll(-n); + } + redrawNLine(buf, n); + } + else + redrawBuffer(buf); + cline = buf->topLine; + ccolumn = buf->currentColumn; + } + if (buf->topLine == NULL) + buf->topLine = buf->firstLine; + +#ifdef MOUSE + if (use_mouse) +#if LANG == JA + msg = Strnew_charp("�㢬��"); +#else /* LANG != JA */ + msg = Strnew_charp("<=UpDn "); +#endif /* LANG != JA */ + else +#endif /* not MOUSE */ + msg = Strnew(); + Strcat_charp(msg, "Viewing <"); + Strcat_charp(msg, buf->buffername); + if (displayLink) + aa = retrieveCurrentAnchor(buf); + if (aa) { + ParsedURL url; + Str s; + int l; + parseURL2(aa->url, &url, baseURL(buf)); + s = parsedURL2Str(&url); + l = buf->width - 2; + if (s->length > l) { + if (l >= 4) { + msg = Strsubstr(s, 0, (l - 2) / 2); +#if LANG == JA + Strcat_charp(msg, "��"); +#else /* LANG != JA */ + Strcat_charp(msg, ".."); +#endif /* LANG != JA */ + l = buf->width - msg->length; + Strcat(msg, Strsubstr(s, s->length - l, l)); + } else { + msg = s; + } + } else { + l -= s->length; + if (msg->length > l) { +#ifdef JP_CHARSET + char *bn = msg->ptr; + int i, j; + for (i = 0; bn[i]; i += j) { + j = get_mclen(get_mctype(&bn[i])); + if (i + j > l) + break; + } + l = i; +#endif + Strtruncate(msg, l); + } + Strcat_charp(msg, "> "); + Strcat(msg, s); + } + } else { + Strcat_charp(msg, ">"); + } + if (buf->firstLine == NULL) { + Strcat_charp(msg, "\tNo Line"); + clear(); + } + standout(); + message(msg->ptr, buf->cursorX, buf->cursorY); + standend(); + refresh(); +#ifdef BUFINFO + if (Currentbuf != save_current_buf) { + saveBufferInfo(); + save_current_buf = Currentbuf; + } +#endif +} + +void +redrawBuffer(Buffer * buf) +{ + redrawNLine(buf, LASTLINE); +} + +void +redrawNLine(Buffer * buf, int n) +{ + Line *l, *l0; + int i; + +#ifdef COLOR + if (useColor) { + EFFECT_ANCHOR_END_C; +#ifdef BG_COLOR + setbcolor(bg_color); +#endif /* BG_COLOR */ + } +#endif /* COLOR */ + for (i = 0, l = buf->topLine; i < LASTLINE; i++) { + if (i >= LASTLINE - n || i < -n) + l0 = redrawLine(buf, l, i); + else { + l0 = (l) ? l->next : NULL; + } + if (l0 == NULL && l == NULL) + break; + l = l0; + } + if (n > 0) + clrtobotx(); +} + +#define addKanji(pc,pr) (addChar((pc)[0],(pr)[0]),addChar((pc)[1],(pr)[1])) + +Line * +redrawLine(Buffer * buf, Line * l, int i) +{ + int j, pos, rcol, ncol, delta; + int column = buf->currentColumn; + char *p; + Lineprop *pr; +#ifdef ANSI_COLOR + Linecolor *pc; +#endif +#ifdef COLOR + Anchor *a; + ParsedURL url; + int k, vpos = -1; +#endif + + if (l == NULL) { + if (buf->pagerSource) { + l = getNextPage(buf, LASTLINE - i); + if (l == NULL) + return NULL; + } + else + return NULL; + } + move(i, 0); + if (l->width < 0) + l->width = COLPOS(l, l->len); + if (l->len == 0 || l->width - 1 < column) { + clrtoeolx(); + return l->next; + } + /* need_clrtoeol(); */ + pos = columnPos(l, column); + p = &(l->lineBuf[pos]); + pr = &(l->propBuf[pos]); +#ifdef ANSI_COLOR + if (useColor && l->colorBuf) + pc = &(l->colorBuf[pos]); + else + pc = NULL; +#endif + rcol = COLPOS(l, pos); + +#ifndef JP_CHARSET + delta = 1; +#endif + for (j = 0; rcol - column < COLS && pos + j < l->len; j += delta) { +#ifdef COLOR + if (useVisitedColor && vpos <= pos + j && !(pr[j] & PE_VISITED)) { + a = retrieveAnchor(buf->href, l->linenumber, pos + j); + if (a) { + parseURL2(a->url, &url, baseURL(buf)); + if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) { + for (k = a->start.pos; k < a->end.pos; k++) + pr[k - pos] |= PE_VISITED; + } + vpos = a->end.pos; + } + } +#endif +#ifdef JP_CHARSET + if (CharType(pr[j]) == PC_KANJI1) + delta = 2; + else + delta = 1; +#endif + ncol = COLPOS(l, pos + j + delta); + if (ncol - column > COLS) + break; +#ifdef ANSI_COLOR + if (pc) + do_color(pc[j]); +#endif + if (rcol < column) { + for (rcol = column; rcol < ncol; rcol++) + addChar(' ', 0); + continue; + } + if (p[j] == '\t') { + for (; rcol < ncol; rcol++) + addChar(' ', 0); + } +#ifdef JP_CHARSET + else if (delta == 2) { + addKanji(&p[j], &pr[j]); + } +#endif + else { + addChar(p[j], pr[j]); + } + rcol = ncol; + } + if (somode) { + somode = FALSE; + standend(); + } + if (ulmode) { + ulmode = FALSE; + underlineend(); + } + if (bomode) { + bomode = FALSE; + boldend(); + } + if (emph_mode) { + emph_mode = FALSE; + boldend(); + } + + if (anch_mode) { + anch_mode = FALSE; + EFFECT_ANCHOR_END; + } + if (imag_mode) { + imag_mode = FALSE; + EFFECT_IMAGE_END; + } + if (form_mode) { + form_mode = FALSE; + EFFECT_FORM_END; + } + if (visited_mode) { + visited_mode = FALSE; + EFFECT_VISITED_END; + } + if (active_mode) { + active_mode = FALSE; + EFFECT_ACTIVE_END; + } +#ifndef KANJI_SYMBOLS + if (graph_mode) { + graph_mode = FALSE; + graphend(); + } +#endif /* not KANJI_SYMBOLS */ +#ifdef ANSI_COLOR + if (color_mode) + do_color(0); +#endif + if (rcol - column < COLS) + clrtoeolx(); + return l->next; +} + +int +redrawLineRegion(Buffer * buf, Line * l, int i, int bpos, int epos) +{ + int j, pos, rcol, ncol, delta; + int column = buf->currentColumn; + char *p; + Lineprop *pr; +#ifdef ANSI_COLOR + Linecolor *pc; +#endif + int bcol, ecol; +#ifdef COLOR + Anchor *a; + ParsedURL url; + int k, vpos = -1; +#endif + + if (l == NULL) + return 0; + pos = columnPos(l, column); + p = &(l->lineBuf[pos]); + pr = &(l->propBuf[pos]); +#ifdef ANSI_COLOR + if (useColor && l->colorBuf) + pc = &(l->colorBuf[pos]); + else + pc = NULL; +#endif + rcol = COLPOS(l, pos); + bcol = bpos - pos; + ecol = epos - pos; + +#ifndef JP_CHARSET + delta = 1; +#endif + for (j = 0; rcol - column < COLS && pos + j < l->len; j += delta) { +#ifdef COLOR + if (useVisitedColor && vpos <= pos + j && !(pr[j] & PE_VISITED)) { + a = retrieveAnchor(buf->href, l->linenumber, pos + j); + if (a) { + parseURL2(a->url, &url, baseURL(buf)); + if (getHashHist(URLHist, parsedURL2Str(&url)->ptr)) { + for (k = a->start.pos; k < a->end.pos; k++) + pr[k - pos] |= PE_VISITED; + } + vpos = a->end.pos; + } + } +#endif +#ifdef JP_CHARSET + if (CharType(pr[j]) == PC_KANJI1) + delta = 2; + else + delta = 1; +#endif + ncol = COLPOS(l, pos + j + delta); + if (ncol - column > COLS) + break; +#ifdef ANSI_COLOR + if (pc) + do_color(pc[j]); +#endif + if (j >= bcol && j < ecol) { + if (rcol < column) { + move(i, 0); + for (rcol = column; rcol < ncol; rcol++) + addChar(' ', 0); + continue; + } + move(i, rcol - column); + if (p[j] == '\t') { + for (; rcol < ncol; rcol++) + addChar(' ', 0); + } +#ifdef JP_CHARSET + else if (delta == 2) { + addKanji(&p[j], &pr[j]); + } +#endif + else { + addChar(p[j], pr[j]); + } + } + rcol = ncol; + } + if (somode) { + somode = FALSE; + standend(); + } + if (ulmode) { + ulmode = FALSE; + underlineend(); + } + if (bomode) { + bomode = FALSE; + boldend(); + } + if (emph_mode) { + emph_mode = FALSE; + boldend(); + } + + if (anch_mode) { + anch_mode = FALSE; + EFFECT_ANCHOR_END; + } + if (imag_mode) { + imag_mode = FALSE; + EFFECT_IMAGE_END; + } + if (form_mode) { + form_mode = FALSE; + EFFECT_FORM_END; + } + if (visited_mode) { + visited_mode = FALSE; + EFFECT_VISITED_END; + } + if (active_mode) { + active_mode = FALSE; + EFFECT_ACTIVE_END; + } +#ifndef KANJI_SYMBOLS + if (graph_mode) { + graph_mode = FALSE; + graphend(); + } +#endif /* not KANJI_SYMBOLS */ +#ifdef ANSI_COLOR + if (color_mode) + do_color(0); +#endif + return rcol - column; +} + +#define do_effect1(effect,modeflag,action_start,action_end) \ +if (m & effect) { \ + if (!modeflag) { \ + action_start; \ + modeflag = TRUE; \ + } \ +} + +#define do_effect2(effect,modeflag,action_start,action_end) \ +if (modeflag) { \ + action_end; \ + modeflag = FALSE; \ +} + +void +do_effects(Lineprop m) +{ + /* effect end */ + do_effect2(PE_UNDER, ulmode, underline(), underlineend()); + do_effect2(PE_STAND, somode, standout(), standend()); + do_effect2(PE_BOLD, bomode, bold(), boldend()); + do_effect2(PE_EMPH, emph_mode, bold(), boldend()); + do_effect2(PE_ANCHOR, anch_mode, EFFECT_ANCHOR_START, EFFECT_ANCHOR_END); + do_effect2(PE_IMAGE, imag_mode, EFFECT_IMAGE_START, EFFECT_IMAGE_END); + do_effect2(PE_FORM, form_mode, EFFECT_FORM_START, EFFECT_FORM_END); + do_effect2(PE_VISITED, visited_mode, EFFECT_VISITED_START, EFFECT_VISITED_END); + do_effect2(PE_ACTIVE, active_mode, EFFECT_ACTIVE_START, EFFECT_ACTIVE_END); +#ifndef KANJI_SYMBOLS + if (graph_mode) { + graphend(); + graph_mode = FALSE; + } +#endif /* not KANJI_SYMBOLS */ + + /* effect start */ + do_effect1(PE_UNDER, ulmode, underline(), underlineend()); + do_effect1(PE_STAND, somode, standout(), standend()); + do_effect1(PE_BOLD, bomode, bold(), boldend()); + do_effect1(PE_EMPH, emph_mode, bold(), boldend()); + do_effect1(PE_ANCHOR, anch_mode, EFFECT_ANCHOR_START, EFFECT_ANCHOR_END); + do_effect1(PE_IMAGE, imag_mode, EFFECT_IMAGE_START, EFFECT_IMAGE_END); + do_effect1(PE_FORM, form_mode, EFFECT_FORM_START, EFFECT_FORM_END); + do_effect1(PE_VISITED, visited_mode, EFFECT_VISITED_START, EFFECT_VISITED_END); + do_effect1(PE_ACTIVE, active_mode, EFFECT_ACTIVE_START, EFFECT_ACTIVE_END); +#ifndef KANJI_SYMBOLS + if (m & PC_RULE) { + if (!graph_mode && graph_ok()) { + graphstart(); + graph_mode = TRUE; + } + } +#endif /* not KANJI_SYMBOLS */ +} + +#ifdef ANSI_COLOR +void +do_color(Linecolor c) +{ + if (c & 0x8) + setfcolor(c & 0x7); + else if (color_mode & 0x8) + setfcolor(basic_color); +#ifdef BG_COLOR + if (c & 0x80) + setbcolor((c >> 4) & 0x7); + else if (color_mode & 0x80) + setbcolor(bg_color); +#endif + color_mode = c; +} +#endif + +void +addChar(char c, Lineprop mode) +{ + Lineprop m = CharEffect(mode); + +#ifdef JP_CHARSET + if (CharType(mode) != PC_KANJI2) +#endif /* JP_CHARSET */ + do_effects(m); +#ifndef KANJI_SYMBOLS + if (m & PC_RULE) { + if (graph_mode) + addch(g_rule[c & 0xF]); + else + addch(alt_rule[c & 0xF]); + } else +#endif /* not KANJI_SYMBOLS */ + if (IS_UNPRINTABLE_ASCII(c, mode)) { + addstr(Sprintf("\\%3o", (unsigned char)c)->ptr); + } + else if (c == '\t') { + addch(c); + } + else if (c == DEL_CODE) + addstr("^?"); + else if (IS_UNPRINTABLE_CONTROL(c, mode)) { /* Control code */ + addch('^'); + addch(c + '@'); + } + else if (c != '\n') + addch(c); + else /* \n */ + addch(' '); +} + +GeneralList *message_list = NULL; + +void +record_err_message(char *s) +{ + if (fmInitialized) { + if (!message_list) + message_list = newGeneralList(); + if (message_list->nitem >= LINES) + popValue(message_list); + pushValue(message_list, allocStr(s, 0)); + } +} + +/* + * List of error messages + */ +Buffer * +message_list_panel(void) +{ + Str tmp = Strnew_size(LINES * COLS); + ListItem *p; + + Strcat_charp(tmp, + "<html><head><title>List of error messages</title></head><body>" + "<h1>List of error messages</h1><table cellpadding=0>\n"); + if (message_list) + for (p = message_list->last ; p ; p = p->prev) + Strcat_m_charp(tmp, "<tr><td><pre>", htmlquote_str(p->ptr), "</pre></td></tr>\n", NULL); + else + Strcat_charp(tmp, "<tr><td>(no message recorded)</td></tr>\n"); + Strcat_charp(tmp, "</table></body></html>"); + return loadHTMLString(tmp); +} + +void +message(char *s, int return_x, int return_y) +{ + if (!fmInitialized) + return; + move(LASTLINE, 0); + addnstr(s, COLS - 1); + clrtoeolx(); + move(return_y, return_x); +} + +void +disp_message_nsec(char *s, int redraw_current, int sec, int purge, int mouse) +{ + if (!fmInitialized) { + fprintf(stderr, "%s\n", s); + return; + } + if (Currentbuf != NULL) + message(s, Currentbuf->cursorX, Currentbuf->cursorY); + else + message(s, LASTLINE, 0); + refresh(); +#ifdef MOUSE + if (mouse && use_mouse) + mouse_active(); +#endif + sleep_till_anykey(sec, purge); +#ifdef MOUSE + if (mouse && use_mouse) + mouse_inactive(); +#endif + if (Currentbuf != NULL && redraw_current) + displayBuffer(Currentbuf, B_NORMAL); +} + +void +disp_message(char *s, int redraw_current) +{ + disp_message_nsec(s, redraw_current, 10, FALSE, TRUE); +} +#ifdef MOUSE +void +disp_message_nomouse(char *s, int redraw_current) +{ + disp_message_nsec(s, redraw_current, 10, FALSE, FALSE); +} +#endif + +void +cursorUp(Buffer * buf) +{ + if (buf->firstLine == NULL) + return; + if (buf->cursorY > 0) + cursorUpDown(buf, -1); + else { + buf->topLine = lineSkip(buf, buf->topLine, -(LASTLINE + 1) / 2, FALSE); + if (buf->currentLine->prev != NULL) + buf->currentLine = buf->currentLine->prev; + arrangeLine(buf); + } +} + +void +cursorDown(Buffer * buf) +{ + if (buf->firstLine == NULL) + return; + if (buf->cursorY < LASTLINE - 1) + cursorUpDown(buf, 1); + else { + buf->topLine = lineSkip(buf, buf->topLine, (LASTLINE + 1) / 2, FALSE); + if (buf->currentLine->next != NULL) + buf->currentLine = buf->currentLine->next; + arrangeLine(buf); + } +} + +void +cursorUpDown(Buffer * buf, int n) +{ + Line *cl = buf->currentLine; + + if (buf->firstLine == NULL) + return; + if ((buf->currentLine = currentLineSkip(buf, cl, n, FALSE)) == cl) + return; + arrangeLine(buf); +} + +void +cursorRight(Buffer * buf) +{ + int i, delta = 1, cpos, vpos2; + Line *l = buf->currentLine; + Lineprop *p; + + if (buf->firstLine == NULL) + return; + if (buf->pos == l->len) + return; + i = buf->pos; + p = l->propBuf; +#ifdef JP_CHARSET + if (CharType(p[i]) == PC_KANJI1) + delta = 2; +#endif /* JP_CHARSET */ + if (i + delta < l->len) { + buf->pos = i + delta; + } + else if (l->len == 0) { + buf->pos = 0; + } + else { + buf->pos = l->len -1; +#ifdef JP_CHARSET + if (CharType(p[buf->pos]) == PC_KANJI2) + buf->pos--; +#endif /* JP_CHARSET */ + } + cpos = COLPOS(l, buf->pos); + buf->visualpos = cpos - buf->currentColumn; + delta = 1; +#ifdef JP_CHARSET + if (CharType(p[buf->pos]) == PC_KANJI1) + delta = 2; +#endif /* JP_CHARSET */ + vpos2 = COLPOS(l, buf->pos + delta) - buf->currentColumn - 1; + if (vpos2 >= COLS) { + columnSkip(buf, (COLS / 2) + (vpos2 - COLS) - (vpos2 - COLS) % (COLS / 2)); + buf->visualpos = cpos - buf->currentColumn; + } + buf->cursorX = buf->visualpos; +} + +void +cursorLeft(Buffer * buf) +{ + int i, delta = 1, cpos; + Line *l = buf->currentLine; + Lineprop *p; + + if (buf->firstLine == NULL) + return; + i = buf->pos; + p = l->propBuf; +#ifdef JP_CHARSET + if (i >= 2 && CharType(p[i - 1]) == PC_KANJI2) + delta = 2; +#endif /* JP_CHARSET */ + if (i > delta) + buf->pos = i - delta; + else + buf->pos = 0; + cpos = COLPOS(l, buf->pos); + buf->visualpos = cpos - buf->currentColumn; + if (buf->visualpos < 0) { + columnSkip(buf, -(COLS / 2) + buf->visualpos - buf->visualpos % (COLS / 2)); + buf->visualpos = cpos - buf->currentColumn; + } + buf->cursorX = buf->visualpos; +} + +void +cursorHome(Buffer * buf) +{ + buf->visualpos = 0; + buf->cursorX = buf->cursorY = 0; +} + + +/* + * Arrange line,column and cursor position according to current line and + * current position. + */ +void +arrangeCursor(Buffer * buf) +{ + int col,col2; + int delta = 1; + if (buf == NULL || buf->currentLine == NULL) + return; + /* Arrange line */ + if (buf->currentLine->linenumber - buf->topLine->linenumber >= LASTLINE || + buf->currentLine->linenumber < buf->topLine->linenumber) { + buf->topLine = buf->currentLine; + } + /* Arrange column */ + if (buf->currentLine->len == 0) + buf->pos = 0; + else if (buf->pos >= buf->currentLine->len) + buf->pos = buf->currentLine->len - 1; +#ifdef JP_CHARSET + if (CharType(buf->currentLine->propBuf[buf->pos]) == PC_KANJI2) + buf->pos--; +#endif /* JP_CHARSET */ + col = COLPOS(buf->currentLine, buf->pos); +#ifdef JP_CHARSET + if (CharType(buf->currentLine->propBuf[buf->pos]) == PC_KANJI1) + delta = 2; +#endif /* JP_CHARSET */ + col2 = COLPOS(buf->currentLine, buf->pos + delta); + if (col < buf->currentColumn || col2 > COLS + buf->currentColumn) { + buf->currentColumn = 0; + if (col2 > COLS) + columnSkip(buf, col); + } + /* Arrange cursor */ + buf->cursorY = buf->currentLine->linenumber - buf->topLine->linenumber; + buf->visualpos = buf->cursorX = COLPOS(buf->currentLine, buf->pos) - buf->currentColumn; +#ifdef DISPLAY_DEBUG + fprintf(stderr, "arrangeCursor: column=%d, cursorX=%d, visualpos=%d, pos=%d, len=%d\n", + buf->currentColumn, buf->cursorX, buf->visualpos, + buf->pos, buf->currentLine->len); +#endif +} + +void +arrangeLine(Buffer * buf) +{ + int i, cpos; + + if (buf->firstLine == NULL) + return; + buf->cursorY = buf->currentLine->linenumber - buf->topLine->linenumber; + i = columnPos(buf->currentLine, buf->currentColumn + buf->visualpos); + cpos = COLPOS(buf->currentLine, i) - buf->currentColumn; + if (cpos >= 0) { + buf->cursorX = cpos; + buf->pos = i; + } + else if (Currentbuf->currentLine->len > i) { + int delta = 1; +#ifdef JP_CHARSET + if (Currentbuf->currentLine->len > i + 1 && + CharType(buf->currentLine->propBuf[i + 1]) == PC_KANJI2) + delta = 2; +#endif + buf->cursorX = 0; + buf->pos = i; + if (COLPOS(buf->currentLine, i + delta) <= buf->currentColumn) + buf->pos += delta; + } + else { + buf->cursorX = 0; + buf->pos = 0; + } +#ifdef DISPLAY_DEBUG + fprintf(stderr, "arrangeLine: column=%d, cursorX=%d, visualpos=%d, pos=%d, len=%d\n", + buf->currentColumn, buf->cursorX, buf->visualpos, + buf->pos, buf->currentLine->len); +#endif +} + +void +cursorXY(Buffer * buf, int x, int y) +{ + int oldX; + + cursorUpDown(buf, y - buf->cursorY); + + if (buf->cursorX > x) { + while (buf->cursorX > x) + cursorLeft(buf); + } + else if (buf->cursorX < x) { + while (buf->cursorX < x) { + oldX = buf->cursorX; + + cursorRight(buf); + + if (oldX == buf->cursorX) + break; + } + if (buf->cursorX > x) + cursorLeft(buf); + } +} + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ diff --git a/doc-jp/FAQ.html b/doc-jp/FAQ.html new file mode 100644 index 0000000..257f512 --- /dev/null +++ b/doc-jp/FAQ.html @@ -0,0 +1,238 @@ +<HTML> +<HEAD> +<TITLE>W3M FAQ</TITLE> +</HEAD> +<BODY> +<h1>w3m�˴ؤ����ɤ�ʹ�����(�Ǥ�����)����Ȥ�������</h1> +<div align=right> +��ƣ ��§<br> +aito@ei5sun.yz.yamagata-u.ac.jp +</div> +<a name="general"> +<center> +<h2>����Ū�ʤ��ȡ�������ˡ��ư��Ķ�</h2> +</center> +<dl> +<dt>``w3m''�ϲ����ɤ�ΤǤ����� +<dd>�֤��֤�塼����פޤ��ϡ֤��֤�塼�������פǤ��� +``w3m''�Ƚƥץƥ�Υɥ���ɤ����Ϥ��ޤ��� +<P> +<dt>�ɤ�����``w3m''�Ȥ���̾���ʤΡ� +<dd>WWW-wo-Miru(WWW��)�����դ��ޤ����� +<P> +<dt>�ɤ������Ķ���ư���Ρ� +<dd>����Ū��UNIX��ư���ޤ����ǿ��Ǥޤ��Ϥ���˶ᤤ�С�������ư���ǧ����Ƥ���Τϡ� +<blockquote> +<pre> +Solaris 2.5 �ʾ� +SunOS 4.1.x +HP-UX 9.x, 10.x +Linux 2.0.30 +FreeBSD 2.2.8, 3.1 +EWS4800 Release12.2 Rev.A +</pre></blockquote> +�ʤɤǤ�������¾�Τ�ΤǤ⡤��㡼��UNIX�����ƥ�ʤ�ư���Ǥ��礦�� +<P> +version 990226 ���顤OS/2 ��ư���褦�Ǥ��� +<P> +version 990303 ���顤Windows+cygwin32 ��ư���褦�ˤʤ�ޤ����� +<P> +<dt>Windows 9x/NT �Ǥ�ư���ʤ��Ρ� +<dd><a href="http://sourceware.cygnus.com/cygwin/">Cygwin32</a>��Ȥ���ư���ޤ��� +<p> +<dt>w3m�˴ؤ������Ϥɤ�����������Ρ� +<dd><a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/"> +http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/</a>�� w3m �Υڡ��� +������ޤ��� +<p> +<dt>�ǿ��ǤϤɤ�����������Ρ� +<dd><a href="ftp://ei5nazha.yz.yamagata-u.ac.jp/w3m/"> +ftp://ei5nazha.yz.yamagata-u.ac.jp/w3m/</a>����������ޤ��� +<p> +<dt>w3m �˴ؤ���ML�Ϥ���ޤ��� +<dd> +��ȯ�Ը���ML(w3m-dev(���ܸ�)��w3m-dev-en(�Ѹ�))������ޤ����ܤ����� +<a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/">w3m �Υڡ���</a> +����������������ȯ��Ϣ�ʳ��� +���������������Ȥ�������ˤϡ���Ԥ�<a href="mailto:aito@ei5sun.yz.yamagata-u.ac.jp"> +ľ�ܥ��</a>���뤫�����뤤��<a href="http://ei5nazha.yz.yamagata-u.ac.jp/BBS/spool/log.html"> +��Ԥα��Ĥ���Ǽ���</a>�˽Ƥ��������� +<p> +<dt>�Х��ʥ����ۤϤ��ʤ��Ρ� +<dd> +�����Ĥ��Υץ�åȥե�����ˤĤ��ơ�w3m�ΥХ��ʥ����ۤ��ä� +�������äƤ��륵���Ȥ�����ޤ����ܤ����� +<a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/"> +w3m�Υڡ���</a>��������� + +</dl> + +<a name="install"></a> +<center><h2>����ѥ���ȥ��ȡ���</h2></center> +�ä�����ʤ� :-) + +<a name="command"></a> +<center><h2>���ץ�����ޥ�ɡ��Ȥ�����</h2></center> +<dl> +<dt> w3m �����Ϥ����顤����ɽ�������˽���ä��㤤�ޤ��������������Ρ� +<dd> w3m ��<B>�ڡ�����</B>�Ǥ����Ǥ����顤������ꤷ�ʤ��ǵ�ư����� +���Τޤ�λ���ޤ�������ɽ�������������ˤϡ� +<ol> +<li>�����˥ե�����̾��URL��� +<li>ɸ�����Ϥ˲�������Ƥ����롥 +<li>-B ���ץ����(�֥å��ޡ���ɽ��)��Ĥ��롥 +<li>�Ķ��ѿ� HTTP_HOME �� WWW_HOME �ˡ��������ڡ�����URL������Ƥ����� +</ol> +�Τɤ줫���äƤ��������� +<p> +<dt>w3m��ư�����顤���̤����ù��ˤʤäƤ��ޤ��ޤ������ɤ����ơ� +<dd>w3m�顼ɽ��������ǥ���ѥ��뤹��ȡ��ǽ���طʿ������ +ʸ��������������ˤʤ�ޤ������Τ��ᡤ�դ����طʤ���ˤ��Ƥ�����֤� +w3m��ư����ȡ����������ʤ��ʤ�ޤ���<p> +�����������ϡ����Τ褦�ˤ��ƿ������ꤷ�ޤ��� +<UL> +<LI>w3m -M �� w3m ��ư��������⡼�ɤ�ɽ�����롥 +<LI>"o" ���ޥ�ɤǥ��ץ����������̤ˤ��롥 +<LI><B>���顼ɽ���⡼�ɤ�ON�ˤ�</B>��Ŭ����ʸ����������ǡ� +[OK]�����롥 +</UL> +<dt>���顼ɽ������ˤϤɤ�����Ρ� +<dd>����ѥ�����ˡ�configure �μ��� +<p> +<pre> +Let's do some configurations. Choose config option among the list." + +1 - Baby model (no color, no menu, no mouse, no cookie, no SSL) +2 - Little model (color, menu, no mouse, no cookie, no SSL) +3 - Mouse model (color, menu, mouse, no cookie, no SSL) +4 - Cookie model (color, menu, mouse, cookie, no SSL) +5 - Monster model (with everything; you need openSSL library) +6 - Customize +Which? +</pre> +<p> +�ǡ�2,3,4,5�Τɤ줫��������Х��顼ɽ�����Ǥ���褦�ˤʤ�ޤ��� +<p> +<dt>�����ɽ��������������ɡ� +<dd>��ˡ��3�Ĥ���ޤ��� +<OL> +<LI>�嵭�μ���� 1 �������ƥ���ѥ��뤹�롥 +<LI>-M ���ץ�����Ĥ��Ƶ�ư���롥 +<LI>"o" ���ޥ�ɤǥ��ץ��������ѥͥ��ư�������顼ɽ����OFF�ˤ��롥 +</OL> +<dt>���̤�Ϥ߽Ф�����ʬ��ˤϡ� +<dd>�����������̤�ü�˰�ư������С�����˹�碌�Ʋ������Τ�����ޤ��� +�ޤ���">"��"<"�Dz������Τ餹���Ȥ��Ǥ��ޤ��� +<p> +<dt>���������ư���鷺��路���� +<dd>TAB�Ǽ��Υ����˰�ư����Τǡ�Lynx�Ȼ������ФǻȤ���Ǥ��礦�� +�ޤ���C-u�����ESC TAB�����Υ��������ޤ��� +<p> +<dt>Netscape�Ǥ��֤�ʸ���ˤʤäƤ�����ʬ����w3m �ǤϹ����ޤޡ��ʤ��� +<dd>w3m�ϡ�<FONT COLOR="..">�ˤ��ʸ���ο�����ˤ��б����Ƥ��ޤ��� +�б����Բ�ǽ�ǤϤʤ��Ǥ�����ʸ���ο����طʤ�Ʊ���ˤʤä��ꤷ�Ƹ��Ť餯 +�ʤ�Τ������ʤΤǡ����ޤ��б����뵤�ˤʤ�ޤ��� +<p> +<dt>����/����/form�ο����Ѥ���ˤϡ� +<dd>990309�Ǥ��顤���ץ������ڤ꤫������褦�ˤʤ�ޤ�����"o" ���ޥ�� +������ѥͥ��ɽ�������������ʿ�������Ǥ�������������ɽ�������طʤ�Ʊ���� +�ʤäƲ��⸫���ʤ��Ȥ������ˤϡ�-M ���ץ��������ɽ���ˤ��Ƥ�������� +����Ȥ褤�Ǥ��礦�� +<p> +<dt>�Ķ��ѿ� EDITOR �����ꤷ�����ɡ������ʤ��ΤϤʤ��� +<dd>"o"���ޥ�ɤǥ��ץ�����ɽ�������ƤߤƤ��������� +���ǥ����ι��ܤ˲����Ƥ���С����줬ͥ�褵��ޤ��� +�Ķ��ѿ���������������ϡ�����������ˤ��ƥ��ץ����� +�������Ƥ��������� +<p> +<dt>��������URL���Ϥ�����Ǥ���ˤϡ� +<dd><!-- C-u ���������Ƥ�ä��ơ�RET���ޤ���--> + C-c �����ޤ��� + +</dl> + +<a name="www"></a> +<center><h2>WWW��Ȥ����μ���</h2></center> +<dl> +<dt>form�����Ϥ���ˤϤɤ�����Ρ� +<dd>form����ʬ�ϡ����̾����(�ޤ���ȿž)��ɽ������Ƥ��ޤ��Τǡ� +�����˥����������äƤ��äƥ������ޤ�������ȡ� +<UL> +<LI>text �ξ��ϡ����̺Dz��Ԥ������Ԥ��ˤʤ�ޤ��Τǡ�ʸ�������Ϥ��ޤ��� +<LI>radio, checkbox �ξ��ϡ����ι��ܤ����Ф�ޤ��� +<LI>textarea �ξ��ϡ����ǥ�������ư���ޤ��Τǡ�ʸ�Ϥ����Ϥ��ޤ��� +���ΤȤ���ɬ��ɽ���Ѵ��������ɤ�Ʊ�����������ɤǥե��������¸���Ƥ��������� +<LI>submit, reset �ξ��ϡ�form�����Ƥ�����/���ꥢ���ޤ��� +</UL> +<dt>ʸ���ɽ�����٤�������ɡ� +<dd>w3m��HTMLʸ���2�ѥ�����������Τǡ�ʸ�����Τ��ɤߤ��ޤʤ��� +ɽ�����Ǥ��ޤ���Netscape�ʤɤ�ʸ����ɤߤʤ���ɽ������Τǡ� +ɽ����®���褦�˻פ���ΤǤ��礦�� +<p> +<dt>�����ɤ��ʸ���2���ܤ��ɤ���Ȥ��ˡ��ɤߤ��ߤ�®���ʤ�ʤ�������ɡ� +<dd>¾��¿���Υ֥饦���Ȱ㤤��w3m�ϥ���å������äƤ��ޤ��� +���Τ��ᡤʸ����ɤि�Ӥ�WWW�����Ф���ʸ���ž�����ޤ����⤷��ǽ�ʤ顤 +����å��奵���Ф����Ѥ���Ȳ�Ŭ�Ǥ�������ϥץ������������Ʊ���Ǥ��� +<p> +<dt>�����Υե������ľ����¸������ˡ�Ϥʤ��Ρ� +<dd>'a' (Lynx �������Х���ɤξ��� 'd') �ޤ��� ESC RET�ǥ�����ʸ��� +��¸���ޤ�����������¸������� ESC I �Ǥ��� +<p> +<dt>�ץ�����������Ϥɤ�����Ρ� +<dd>�Ķ��ѿ� HTTP_proxy �����ꤹ�뤫��"o" ���ޥ�ɤΥ��ץ��������ѥͥ� +�����ꤷ�ޤ����㤨�� proxy.hogege.com �Ȥ����ۥ��Ȥ� 8000�֥ݡ��Ȥ� +���Ѥ����硤 +<p> +<pre> + http://proxy.hogege.com:8000/ +</pre> +<p> +�����ꤷ�ޤ��� +<p> +<dt>�����֥饦����ư����ȡ�w3m���ǤޤäƤ��ޤ��ޤ��� +���Ȥ��ʤ�ޤ��� +<dd>"o"���ޥ�ɤ�����ѥͥ��ɽ�����������֥饦���ι��ܤˡ��㤨�� +<p> +<pre> + netscape %s & +</pre> +<p> +�Τ褦�����Ϥ��ޤ������ξ�硤%s ����ʬ�� URL ���֤�����äƥ֥饦������ư�� +�ޤ��� +<p> +<dt>�����Υӥ塼�����Ѥ�����������ɡ��ɤ�����Ρ� +<dd>�������Ǥϲ�����Τ� xv ��Ȥ��褦�ˤʤäƤ��ޤ���������� +�㤨�� display ���Ѥ�����ϡ�~/.mailcap �ޤ��� /etc/mailcap �˼��Τ� +���ʵ��Ҥ�����ޤ��� +<p> +<pre> +image/*; display %s +</pre> +<p> +Ʊ���褦�ˡ�¾�Υ����פΥǡ������������ץ���������ꤹ�뤳�Ȥ� +�Ǥ��ޤ��� +<p> +<pre> +image/*; display %s +application/postscript; ghostview %s +application/x-dvi; xdvi %s +</pre> + +</dl> + +<a name="other"></a> +<center><h2>����¾</h2></center> +<dl> +<dt>����ե�����Ϥɤ��ˤ���Ρ� +<dd>~/.w3m �ǥ��쥯�ȥ�β��� config �Ǥ��� +<p> +<dt>~/.w3m �β��� w3mXXXXXX �Τ褦�ʥե����뤬�������뤱�ɡ����� +<dd>WWW�����Ф���ե�������ɤ�Ǥ���Ȥ��ˡ��������Ū����¸���� +�ե�����Ǥ��������륭��å���ե�����ǤϤ���ޤ��� +w3m ��λ����оõ���Ϥ��Ǥ�����w3m���۾ェλ�������ˤϻĤ� +���Ȥ�����ޤ��������������ϼ�Ǿä��Ƥ��������� +<p> +</dl> + +</BODY> +</HTML> diff --git a/doc-jp/HISTORY b/doc-jp/HISTORY new file mode 100644 index 0000000..ce90974 --- /dev/null +++ b/doc-jp/HISTORY @@ -0,0 +1,4273 @@ +2001/3/23 ============================================================== +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01807] Re: w3m-0.2.0 +* url.c �� USE_NNTP �� __EMX__ �ǥ���ѥ���Ǥ��ʤ��� +* EWS4800 �Ѥ� patch (�֤˹礤�ޤ���Ǥ����͡��京����) +* ssl_forbid_method ����ߤ� #define USE_SSL �� #undef USE_SSL_VERIFY + �ξ��ν�����(rc.c �� url.c) + # hsaka24 �ǥ��ʥ������˽���������Ƥ��ޤä��Τ�����Ǥ����͡� + # ���ߤޤ��� +* rc.c �˰��� ISO-2022-JP �������Τ����� + # ź�դ� patch �Ǥ����뤫�ɤ����� +* saveBufferDelNum �� del==TRUE �λ���":" ����������������롣 +* main.c �� URL�������¸������֤ν����� + # ����� hsaka24 �ǥ��ʥ������˽���������Ƥޤ����� + +From: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp> +Subject: [w3m-dev 01810] deflate (was: w3m-0.2.0) +0.2.0 �ˤ� Content-encoding: deflate ���б����뤿��Υѥå� [w3m-dev 01684] +��ޤޤ�Ƥ���褦�Ǥ�������������Ǥ� http://cvs.m17n.org/~akr/diary/ +�ϱ����Ǥ��ޤ���Ǥ����� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01808] Re: w3m-0.2.0 +GNU/Linux �� glibc 2.2�Ϥ��� sin.ss_len ���ʤ��Τ� +IPv6 �ǥ���ѥ���Ǥ��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00399] Re: w3m-0.2.0 + >> From: Dan Fandrich <dan@coneharvesters.com> + >> Version 0.2.0 still contains the following bugs which I fixed two months + >> ago and sent patches for to this list, namely: + >> - core dumps on startup if given a URL requiring a needsterminal mailcap + >> handler + >> - destroys most of an existing ~/.mailcap file without warning when editing + >> - mailcap handling is still wrong as MIME type should be case insensitive + >> - private mailcap extension has an illegal name + +From: SATO Seichi <seichi@as.airnet.ne.jp> +Subject: w3m������ɽ�������ˤ�����Х� +����ʸ����Ȥ��� $* ���Ϥ��� Segmentation fault �� +ȯ������褦�Ǥ���(����̵��̣��ʸ����ʤ�Ǥ���) + +2001/3/22 ============================================================== + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01664] Re: Patch for anonymizer.com +HTTP(HTTPS)�ξ��� URL �� +��http://<host>/<scheme>: ... +�ȤʤäƤ���� cleanupName() ��ƤФʤ��ͤˤ��Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01670] Re: w3m-0.1.11-pre-kokb24-test1 +Str.c �� strcpy/strncpy �� bcopy or memcpy �ˤ����Ǥ����� +bcopy �Ϥ� memcpy �Ϥ����줹��Τϸ�ˤ���Ȥ��Ƥ⡢�Ȥꤢ�������� +bcopy ���֤����������������Ȼפ��ޤ��� +�Ĥ��Ǥˡ�saveBufferDelNum �ǰ��� '\0' �������ʤ��ʤäƤ���Х��ν����Ǥ��� + +From: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp> +Subject: [w3m-dev 01618] backend patch +Subject: [w3m-dev 01671] backend patch for w3m-0.1.11-pre-kokb24-test1 +w3m ������Ū�ʥ��饤����ȤȤ���Ư����ǽ���ɲä���ѥå��Ǥ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01673] SEGV in append_frame_info() +>w3m/0.1.11-pre-kokb23-m17n-0.8 ��ȤäƤ��ޤ�����Der Angriff �Υȥåץڡ��� ( +>http://i.am/goebbels/)�ǡ��ڡ����ξ���褦�ȡ�=�ץ��������顢 +>Segmentation Fault ���Ƥ��ޤ��ޤ����� +�Ȥλ�Ŧ������ޤ�����m17n �Ǥ˸¤�ʤ��Τǡ��Ȥꤢ�����н褷�Ƥ����ޤ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01674] image map +> AREA�Ǻ�ä����饤����ȥ����ɥ�����ޥåפ�href��"#test"�Τ褦�ʾ��ˡ� +> ��������ޤ��� +> Image map links �β��̤Ǥϡ�URL�����Ǥʤ���alt��title��ɽ�����������ɤ��� +> �פ��ޤ��� +�Ȥλ�Ŧ������ޤ����Τǽ���/�б����Ƥߤޤ������������� +* #undef MENU_MAP �ξ�硢#label �ΤߤǤ��äƤ� reload �ˤʤ롣 + �̤ΥХåե�����θƤӽФ��ʤΤǡ��������ʤ��Ȥ���ä����Ǥ��� + �ʤ��������Ǻ��������Хåե�����θƤӽФ������Ƥ����ʤ��ͤˤ��ޤ����� + �Хåե�����ʤ� #define MENU_MAP �������������Ȼפ��ޤ��� +* �ɲä���°���� alt �Τ� (title �äƲ���) + MapList ��¤�Τ��Ѥ��������ɤ��褦�ˤ�פ��ޤ����������ݤʤΤǻߤ�ޤ����� +�ȤʤäƤޤ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01675] goto label +GOTO �� #define MENU_MAP �ξ��Υ�����ޥå� +�� #label �Τߤ� URL �����ꤵ�줿���� reload ���ʤ��ͤˤ��ޤ����� +���줫�顢[w3m-dev 01101] space in URL ���ɲä��줿������ goURL() �˰ܤ� +�ޤ�������������inputLineHist() �� URL �����Ϥ����硢����ʸ���� ^V �� +�Ȥ�ʤ������ϤǤ��ʤ��Τ�ɬ�פʤ��Ȥ�פ��ޤ��������������Ȥ⤢�ä� +����ζ���ν����ϳ����ޤ����� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01676] Re: w3m-0.1.11-pre-kokb24-test1 +Subject: [w3m-dev 01678] Re: w3m-0.1.11-pre-kokb24-test1 +��ƣ����� [w3m-dev 01627] �Υѥå��Τ�����GC_warn ��Ϣ�Ⱥ٤��ʥ����� +�ν��������ƤƤ������ۤ��������褦�˻פ��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01680] Re: w3m-0.1.11-pre-kokb24-test1 + >> ���ĤǤ��� + >> ��δĶ��Ǥϡ�-pedantic �ˤ�ä� + >> warning: ANSI forbids assignment between function pointer and `void *' + >> warning: pointer targets in initialization differ in signedness +���������ʤ�������Ǥ��͡����äƤ��ޤä�... + >> warning: overflow in implicit constant conmplicit con version + >> warning: pointer targets in passing arg 2 of `Strcat_charp_n' differ in signedness + >> �Ȥ����ٹ𤬽Фޤ������������⽤�����٤��Ǥ��礦���� +��������ˤ��������ȤϤʤ��Τ� patch ��Ф��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01684] Re: http://cvs.m17n.org/~akr/diary/ +application/x-deflate �б��� + +From: Moritz Barsnick <barsnick@gmx.net> +Subject: [w3m-dev-en 00318] Information about current page +Subject: [w3m-dev-en 00320] Re: Information about current page +Subject: [w3m-dev-en 00322] Re: Information about current page +Subject: [w3m-dev-en 00323] Buglet (Was: Re: Information about current page) +Changes 'URL of the current anchor' on the info page into +'full' URL. When the cursor is on a form element, +`Method/type of current form' will be displayed. + +From: c603273@vus069.trl.telstra.com.au (Brian Keck) +Subject: [w3m-dev-en 00343] patch for proxy user:passwd on command line +Subject: [w3m-dev-en 00351] Re: patch for proxy user:passwd on command line +This patch to w3m-0.1.11-pre-kokb23 adds the lynx-like option + + -pauth username:password + +so I don't have to retype username & password every time I run w3m, +which is often. It's so simple I wonder whether it's against policy, +but it would be nice for me & some others if it was in the official +0.1.11. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01772] Re: visited anchor +Subject: [w3m-dev 01773] Re: visited anchor + * visited anhor color�� + * textlist �١����� history��hash �����줿 (URL) history�� + * #undef KANJI_SYMBOLS �ξ��� rule �μ������ѹ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01786] Re: w3m-0.1.11-pre-hsaka24 +Subject: [w3m-dev 01787] Re: w3m-0.1.11-pre-hsaka24 + >> 1. http://www.tomoya.com/ �ǡ�<FRAMESET> ��ʸ���ɽ������(�ե졼��μ� + >> ưɽ���� ON �ʤ顢F ��)��MAIN �Υե졼���ɽ�������褦�Ȥ���ȡ� + >> main.c:2082 �� Sprintf �������(gotoLabel �� label=0x0 �ǸƤӤ����Ƥ� + >> ��)�� +[w3m-dev 01675] ����̿Ū�ʥХ������ߤޤ��� + >> 2. �Ǽ��Ħ� http://133.5.222.232/keijiban/index.htm ��ɽ�������褦�Ȥ� + >> ��ȡ�frame.c:668 �� strcasecmp ������� +���ä����������顣 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01788] Re: w3m-0.1.11-pre-hsaka24 +w3m-0.1.11-pre-hsaka24 �ΥХ������Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01792] Re: w3m-0.1.11-pre-hsaka24 + >> �����̷�ʤΤǤ�����useVisitedColor �� TRUE �ΤȤ� + >> http://www.kusastro.kyoto-u.ac.jp/%7Ebaba/wais/other-system.html �ˤ� + >> ���ơ��Ǹ�β��̤�ɽ���� 1��2 �äۤ��Ԥ�����ޤ�������Ū�ˤϡ�goLineL +retrieveAnchor() �� linear search ���ä��Τ������Ǥ����� +binary search ���Ѥ��Ƥߤޤ������ɤ��Ǥ��礦�� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01793] <li type=".."> +<li> ������ type °���Ǥ��������� <li> �ˤΤ�ͭ���ʤΤǤϤʤ��ơ� +<ol> �� <ul> �Ǥλ��������(�ʹߤ� <li> �ˤ�ͭ���Ȥʤ�)�� +�Ǥ��Τǽ������ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01801] some fixes. +frame ��ɽ�����Ƥ��ơ����Ť˽��Ϥ���Ƥ�����ʬ������ޤ����� +���ν����Ǥ��� + +Subject: IPv6 support for w3m's ftp +From: Hajimu UMEMOTO <ume@imasy.or.jp> + w3m �� HTTP �� IPv6 �б�����Ƥ���ΤǤ�������ǰ�ʤ��� FTP ��ǽ���� +�� IPv6 �б����Ƥ��ޤ���FTP ��ǽ���Ф��� IPv6 �б��ѥå���������ޤ� +���Τǡ�������ѥå��˴ޤ��ĺ���ʤ��Ǥ��礦���� + +2001/3/16 ================================================================== +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01711] Authorization +��http://user:pass@hostname/ ���б��� + +From: hsaka@mth.biglobe.ne.jp +Subject: [w3m-dev 01724] buf->type when mailcap is used. +mailcap ��Ȥä����(copiousoutput, htmloutput) �ˤ⡢ +buf->type �����ꤹ���ͤˤ��ޤ����� +'v', 'E' ��Ȥ������Τȡ�m17n �����ǥХåե��� text/html ���Ȥ��� +����ɬ�פʲս꤬����Τǡ� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01726] anchor jump too slow by TAB-key on STDIN. +ɸ�����Ϥ���HTML���ɤ�Ǥ����硤 +������ URL ������ path ������ä��ꤹ��ȡ���ˡ�currentdir() �� +�ƤФ�Ƥ��뤿���®�٤��㲼���Ƥ��ޤ����� +������Ω���夲�������ǰ��� �����ȥǥ��쥯�ȥ�����ꤹ���ͤˤ��ޤ����� +�Ĥ��Ǥ�ɸ�����Ϥ���ξ����Ѥ� URL "file:///-" �� "-" �����ˤ��ޤ����� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01727] C-z when stdin +% cat main.c | w3m +���ơ�C-z �����ݤ˥�����ץ���ץȤ����ʤ�����Ф���ѥå� +�Ǥ��� + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01729] ignore_null_img_alt +ignore_null_img_alt �� OFF ���ȡ�<img src="file"> �Ȥ��� ALT °���� +̵�����Ǥⲿ��ɽ�����ʤ��ʤäƤ����Τǽ������ޤ����� +���줫�顢<img width=300 height=1 src="bar.gif"> ���ͤʻ���ξ��ˡ� +<hr> ���֤�������ȡ�width °����̵�뤵����Ԥ����äƤ��ޤ��Τ� +���ޤ����ʤΤ� <hr> ��Ʊ�ͤν�����������ͤˤ��ޤ����� + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01730] Re: <hr> in a table +<hr>�β��ɡ� + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01731] completion list +�Dz������ϤǤΥե�����̾���䴰���� + +----- Completion list ----- +X11R6/ compat/ include/ libdata/ local/ nfs/ ports/ share/ +bin/ games/ lib/ libexec/ mdec/ obj/ sbin/ src/ +(Load)Filename? /usr/ + +���ͤ�ɽ�������뤿��� patch �Ǥ��� + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01733] A patch concerning SSL +SSL��Ȥ����� +1. �Ȥ�ʤ���åɤ���ꤹ�륪�ץ�����ssl_forbid_method�פ��ɲä��롢 +2. ��³��Ω�˼��Ԥ����Ȥ��˥��顼��å�������ɽ�����롢 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01735] Re: A patch concerning SSL +Subject: [w3m-dev 01737] Re: A patch concerning SSL +1. ssl_forbid_method�Ρ֥ǡ������פ�P_STRING����P_SSLPATH���Ѥ��ơ��� + ư��Υ��ץ��������ѥͥ�ˤ���ѹ��Ǥ�SSL��³�˻Ȥ����åɤ� + �����ȿ�Ǥ����褦�ˤ����� +2. �Ƽ泌�顼��å����������ټ�äƤ����Ƹ�Ǹ����褦�ˤ���(mule + 2.3 base on emacs 19.34�ε�ǽ�Υѥ��ꡢ¾��emacsen�ˤ��뤫���Τ餺)�� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01738] [w3m-doc] w3m document maintenance +w3mϢ��Ģ(http://mi.med.tohoku.ac.jp/~satodai/w3m/bbs/spool/log.html) +�ˤ�ޤ����������ƤǤ��ä�w3m�Υɥ������������Ϥ +���Ȼפ��ޤ��� + +From: kiwamu <kiwamu@debian.or.jp> +Subject: [w3m-dev 01739] �ۥ�����ޥ����б� patch +w3m��ۥ�����ޥ����б������Ƥߤޤ����� +rxvt��xterm�ǻ��ѤǤ��ޤ��� +kterm���ȥۥ�����ξ岼��Ʊ������ȥ����륳���ɤ��֤��Ƥ��ޤ��Τ� +�¸��Բ�ǽ�ߤ����Ǥ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01742] w3mmee 0.1.11p16-6 segfault +w3mmee 0.1.11p16-6 �Ǥ�����mime.types �����Ƥˤ�äƤ� segfault ���ޤ��� +# ���Ԥ�����ȥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01752] SEGV in search_param() + > >> ��w3m -o 1 ���� SEGV ���ޤ��� + > search_param() �� size_t �� unsigned �Τ��� i = 0 �ΤȤ� + > e = 4294967295 �ˤʤäƤ��ޤäƤ���褦�Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01753] empty <select> +<select>��</select> �� <option> ��̵����硢 + +<form action=A> +<select name=B></select> +<input type=submit> +</form> + +���ͤʾ��ˡ�SUBMIT ������ޤ��Τǽ��� patch �Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01754] A search does not stop. +ɸ�����Ϥ����礭�ʥե�������ɤ�Ǥ�������ޤ��֤������� ON ���� +�������ҥåȤ��ʤ�����̵�¥롼�פˤʤ�Х��������ޤ����� + +�ޤ���[w3m-dev 01617] ��ȴ����Ǥ����������Υ���������֤�ư��� +¾�ȹ�碌�ޤ����� + +From: WATANABE Katsuyuki <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01755] relative path with -bookmark option +* -bookmark ���ץ����ǥ֥å��ޡ����ե��������ꤷ���Ȥ��� + ���Хѥ��ǥե�����̾��Ϳ����ȡ��֥å��ޡ������ɲä��Ǥ��ޤ��� +* -bookmark �ǻ��ꤵ�줿�ե����뤬���Хѥ��ξ��ˤϡ����Хѥ��� + ľ�����ݻ�����褦�ˤ��Ƥߤޤ����� + + +2001/2/7 +From: aito +Subject: [w3m-dev 01722] <hr> in a table +ɽ�����<hr>���Ȥ�Ĥ�ȴ����Х��ν����� + +2001/2/6 +From: aito +Local CGI ��ǧ���Ѥˡ�Local cookie �Ȥ��������ߤ�������ޤ����� +Local cookie ��ư��ϼ��Τ褦�ʤ�ΤǤ��� +��w3m �ϡ��ץ������˸�ͭ�� "Local cookie" ��ư�������롥 +��Local CGI �θƤӽФ��Ǥϡ��Ķ��ѿ� LOCAL_COOKIE ���ͳ���ƥ�����ץ� + �� Local cookie ���Ϥ���롥 +��������ץȤϡ�����θƤӽФ��Ѥ�form��url ����� local cookie ����� + ���ࡥ +��������ץȤ�2���ܰʹߤθƤӽФ��Ǥϡ�CGI�ѥ�����ͳ��cookie �� + �Ķ��ѿ���ͳ�� cookie ��Ʊ�����ɤ��������å�����Ʊ���Ǥʤ��ä��� + ������ư��ʤ��� + +w3mbookmark, w3mhelperpanel �� Local cookie ǧ�ڤ�����ޤ����� + +�Ĥ��Ǥˡ�Linux �� gc library �� /usr/local/lib ���˥��ȡ��뤵��� +�������� gcmain.c ������ѥ���Ǥ��ʤ��ʤäƤ����Τǡ��������Ƥߤޤ����� + + +2001/1/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01667] Re: mailer %s + Editor �� "vi %s" �ʤɤξ��� "vi file +10" �ʤɤ�Ÿ������Ƥ��ޤ��� + ���꤬���ä��Τǡ�Editor ��Ÿ���� + ��%s �������� + * %d ������С� + Sprintf(Editor, linenum, file) # ���֤ϸ��� + * �����Ǥʤ���� + Sprintf(Editor, file) + ��%s ���ʤ���� + * %d ������С� + Sprintf(Editor, linenum) file + * "vi" �Ȥ���ʸ������С� + Sprintf("%s +%d", Editor, linenum) file + * �����Ǥʤ���� + Editor file + �Ȥ��Ƥߤޤ����� + + +2001/1/24 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01661] Re: <head> +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01662] Re: <head> + security fix. + + +2001/1/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��°���ͤ���� ", <, > & �����������Ȥ���Ƥ��뤫�ɤ��������å����� + �褦�ˤ���. + ��°������Ƥʤ�������, °���Υ����å���ȴ���Ƥ�������ν���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01652] mailer %s +Subject: [w3m-dev 01663] replace addUniqHist with addHist in loadHistory() + +2001/1/22 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01617] Re: first body with -m (Re: w3m-m17n-0.7) + ü����ꥵ������������ư���Ʊ���ˤ��ޤ���(ñ��˺��Ƥ�������)�� + �Ĥ��Ǥˡ�¿�ʤΥե졼��ǹ�������Ƥ���ڡ����λ���INFO('=') �Ǥ� + �ե졼������ɽ�������������ä��ΤǤ��ν����Ǥ��� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01621] NEXT_LINK and GOTO_LINE problem + NEXT_LINK �� GOTO_LINE �Ǥ��������Υڡ����κǽ�ιԤ˰�ư�����Ȥ������� + 1 �ڡ���ʬ���������뤷�Ƥ��ޤ��ޤ��� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01623] Re: (frame) http://www.securityfocus.com/ +Subject: [w3m-dev 01632] Re: (frame) http://www.securityfocus.com/ + frame fix. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01624] Re: first body with -m +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01625] Re: first body with -m + pgFore, pgBack �ǡ�currentLine �����̳��Ȥʤꡢ���ġ������ʬ + ����������Ǥ��ʤ��ä��Ȥ��ˡ����ޤ�ɽ������Ƥ�����ʬ�ȿ�����ɽ������ + ����ʬ�δ֤� currentLine ����äƤ���褦�ʥѥå���Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01635] Directory list + local.c �� directory list �����������ʬ�˥Х�������ޤ����� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01643] buffername +Subject: [w3m-dev 01650] Re: buffername + buffername (title) �˴ؤ������(?)�Ƚ����Ǥ��� + ��displayLink �� ON �ξ���Ĺ�� URL ��ɽ��������� buffername ������ + �ڤ�Ĥ���ͤˤ��Ƥߤޤ����� + ���Ĥ��Ǥ� displayBuffer() �Υ����ɤ������� + ��HTML �椫�� title ������������ζ���ʸ���Ϻ�������ͤˤ��ޤ����� + ��[w3m-dev 01503], [w3m-dev 01504] �η�ν��� + buffername �Ͼ�� cleaup_str ������ݻ�����Ƥ��ޤ��� + �ʤ����������Ǥϡ�SJIS �Υե�����̾����ĥե�������ɤ�ȡ� + buffername �� URL �� SJIS �ˤʤäư����뤳�Ȥ����뤫�⤷��ޤ��� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01646] putAnchor + HTML �Υ�������®�٤Υ٥���ޡ����Ƥߤ褦�Ȼפäơ��������� + ��äƤ�ȡ����륵����������®�٤��㲼���뤳�Ȥ�����ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01647] Re: first body with -m + �京���� #label �Ĥ��� URL ����ĥХåե��� reload ����ȡ� + ����������֤�����Ƥ��ޤ���礬����Ȥλ�Ŧ������ޤ����Τǡ� + ���� patch �Ǥ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01651] display column position with LINE_INFO + LINE_INFO(Ctrl-g) �ǥ������֤���Ϥ����ͤˤ��Ƥߤޤ����� + + +2001/1/5 + +From: Ryoji Kato <ryoji.kato@nrj.ericsson.se> +Subject: [w3m-dev 01582] rfc2732 patch + RFC2732 �˵��Ҥ���Ƥ���褦�� URL ��� '[' �� ']' �Ǥ�����줿 + literal IPv6 address ���᤹�롣 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01594] first body with -m (Re: w3m-m17n-0.7) + "-m" ���ץ�����Ĥ���ư�������Ȥ��ˡ����Υإå�����ʸ�δ֤� + ���Ԥν��������������Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01602] Re: first body with -m (Re: w3m-m17n-0.7) + ... + �ɤ����ˡ� + buf->lastLine->linenumber - buf->topLine->linenumber < LASTLINE - 1 + �Ȥ��������ä���Ȥ����Τ��ʡ� + �Ȥ����櫓�� patch ���äƤߤ��ΤǤ���������äȼ���̵���Ǥ��� + �ʤ���pgFore, pgBack �Υ���������֤��������������('J', 'K') + ��Ʊ��ư��ˤ��Ƥ��ޤ������ʤ�� �ؿ� SPC�٤ȡؿ� J�� ��Ʊ���� + vi ��ư��ȤϤ��ä�����äƤ�Ϥ��Ǥ������ɤ��Ǥ��礦�� + �Ĥ��Ǥˡ�reload, edit ���˥���������֤���¸�����������ɤ��Ƥ��ޤ��� + + +2001/1/1 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01584] Re: attribute replacing in frames. (Re: some fixes) + �⤦���١�frame���tag������ñ��ˤ���patch������ޤ��� + + +2000/12/27 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ե�����ν����˶��Ԥ�;ʬ���ɲä��������ν���. + + +2000/12/26 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01560] Re: long URL + >> ���ĤǤ��� + >> PEEK �� PEEK_LINK �Dz��������Ĺ�� URL ����褦�ˡ�prefix ������ + >> ���Ƽ������Ƥߤޤ����� + >> �����ϰ��٤�����ɽ���������ä��ΤǤ�������������ޤ�꤬�褯�狼��ʤ��� + >> ���Τǡ��Ȥꤢ����ɽ����������ʬ����ꤹ����ˡ��ȤäƤ��ޤ���2c �� 3u + >> �����Ϥ���ȡ����ꤵ�줿��ʬ���б����롢URL �ΰ�����ɽ������ޤ��� + >> ��ո����洶�ۤ��Ԥ����Ƥ���ޤ��� + ���������ΤϤɤ��Ǥ��礦�� + Ϣ³���� 'u' �� 'c' �� URL ����ʸ�����ĥ��������뤷�ޤ��� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01570] Re: long URL + ���ܤ���> # ���Ĥ���ΰƤ�����Ƥ⤤�����⤷��ޤ��� + + ���ܤ���� [w3m-dev 1560] ����κ�ʬ��ź�դ��ޤ�������Ĺ�� URL �ξ� + ���ͭ�����Ȼפ��ޤ�(���ޤ���פϤʤ������Ǥ���)�� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01506] compile option of gc.a + NO_DEBUGGING ���դ��� gc.a ��ѥ��뤹��ȡ�gc.a �� w3m �ΥХ� + �ʥꥵ������¿���Ǥ����������ʤ�ޤ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01509] Forward: Bug#79689: No way to view information on SSL certificates + ���ɥ�����Ȥξ����ɽ��('=')�Ǹ��Ƥ� SSL�˴ؤ������������ + �ߤ��ʤ��ΤϳΤ����ᤷ���ʤ� �ȻפäƤ����Τ� Ŭ���ʥѥå� + �Ĥ��äƤߤޤ�����(���ʤꤤ��������) + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01556] Re: ANSI color support (was Re: w3m-m17n-0.4) + ANSI color support. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01535] how to check wait3 in configure. +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01537] Re: how to check wait3 in configure. + BSD/OS 3.1, SunOS 4.1.3 ��, configure �� wait3() �ФǤ��ʤ��� + ��ؤ��н�. + + +2000/12/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01568] <plaintext> bug + <plaintext> ���ޤȤ��ư���Ƥ��ʤ��ä�����ν���. + + +2000/12/22 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01555] Re: some fixes for <select> + <option> �ʤ��� <select> �������������ͤˤ��Ƥ��ޤäƤ��ޤ����� + �ǽ����Ǥ��� + + +2000/12/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��feed_table �Υȡ�����ʬ������� HTMLlineproc0 �ǹԤʤ��褦���ѹ� + ����. + ��HTMLlineproc0 �Υե�����ν�����ᥤ��롼�פǹԤʤ��褦���ѹ��� + ��. + ��table �� <xmp> �� </xmp> �δ֤ˤ��륿�����ä��������������ν� + ��. + ���ե�����Υǡ��������������ɤ��ޤޤ���������Τ�, ����. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01536] Re: <P> in <DL> +Subject: [w3m-dev 01544] Re: <P> in <DL> + ����Τ��� HTML ��, �۾ェλ���������������ؤ��н�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + <a>, <img_alt>, <b>, <u> ���Υ������Ĥ��Ƥ��ʤ��Ȥ�, ��λ�������� + ������褦�ˤ���. + + +2000/12/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ʲ��ΥХ���ե���������. + ��feed_table_tag �� <dt> �����ν������������������ä�. + ��table ��ǥ������Ĥ��Ƥ��ʤ����, �۾ェλ����������ä�. + �ޤ�, <dt> ����ľ��� <p> ��̵�뤹��褦�ˤ���. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01530] returned at a morment. + read_token �� " �ǰϤޤ줿°���ͤν����Dz��Ԥ��åפ��Ƥ��ʤ��� + ���Х��ν���. +Subject: [w3m-dev 01531] coocie check in header from stdin. + cat ��� | w3m -m + �Ȥ��������ޤ��� + + +2000/12/17 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01513] Re: w3m-0.1.11-pre-kokb23 + frame.c �˥Х��Ȼפ���ս꤬����ޤ����� +Subject: [w3m-dev 01515] some fixes for <select> +Subject: [w3m-dev 01516] Re: some fixes for <select> + <select>��<option> �˴ؤ��ƴ��Ĥ��β��ɤ�Ԥ��ޤ����� + ��multiple °�����������Ƥ������ #undef MENU_SELECT �ξ��� + <option> �� value °�������ꤵ��Ƥ��ʤ��� form �Ȥ��Ƥ� + �ͤ������ʤ��Х��ν����� + ��<option> �� label °���ؤ��б��� + ���ǥե���Ȥ� name °�����ͤ� "default" �Ǥ���Τ� <input> �ʤɤ� + ��碌�� "" �ˡ� + ��<option> �� label �� "" �Ǥ����� "???" �ˤʤ�Τ�ߤ�� + # ����Ǥ��ä��ߤ�������ͤ����롣 + ��n_select >= MAX_SELECT �Ȥʤä���硢#undef MENU_SELECT �Υ����ɤ� + �Ȥ����ͤˤ����� + # MAX_SELECT = 100 �ʤΤǤޤ�̵��̣ + + +2000/12/14 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01501] Re: w3m-0.1.11-pre-kokb23 + no menu �ΤȤ��ˤҤȤĤ�������ѥ��륨�顼���Фޤ����Τǡ����ν��� + �ѥå��Ǥ��� + + +2000/12/13 + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01483] Patch to show image URL includes anchor + ����դ�������URL��Ȥ���`u'�Ǥϥ��URL���������ޤ��� + �Ǥ����������Υѥå���Ȥ���`i'�Dz������Τ�Τ�URL��������褦�� + �ʤ�ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01500] fix risky code in url.c + url.c �ˤ��ä��������Τ��륳���ɤ������ޤ����� + local.c �Ϥ��ޤ��ν����Ǥ��� + + +2000/12/12 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01491] bug ? + file.c �ΰʲ�����ʬ�Ǥ��������֤����Ȼפ��ޤ����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �̥�ʸ����ޤ�ʸ������Ф��븡�����Ǥ���褦�ˤ���. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01498] Re: null character + ̵�¥롼�פˤϤޤäƤ��ޤ��ޤ����� + + +2000/12/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��StrmyISgets ��, ñ�Ȥ� '\r' �����Ԥ�ǧ������ʤ��Х��ν���. + �ޤ�, ���ԥ����ɤ�ʥ�ʸ�����Ѵ��� cleanup_line ��ʬΥ����. + ���ڡ�����⡼�ɤ�, �ʥ�ʸ������褦�ˤ���. + ��base64 �� quoted printable �Υǥ����ɽ����� convertline ���� + istream.c �˰�ư. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01487] A string in <textarea> is broken after editing + w3m-0.1.11-pre-kokb21 �κ�����Ǥ�����<textarea> �����ʸ������� + �������ʸ������� ^` ���ͤ�ʸ�������뤳�Ȥ�����ޤ��� +Subject: [w3m-dev 01488] buffer overflow bugs + �Хåե��������С��ե������������������Τ���ʲ����������������ޤ����� + * file.c �� select_option[MAX_SELECT] ��ź���Υ����å���̵���ä��� + �� n_select �� MAX_SELECT ����� + * file.c �� textarea_str[MAX_TEXTAREA] ��ź���Υ����å����Դ������ä��� + �� n_textarea �� MAX_TEXTAREA ����� + * file.c �� form_stack[FORMSTACK_SIZE] ��ź���Υ����å���̵���ä��� + �� forms �˹�碌�� form_stack ��ݥ��Ȥ���ư��ĥ�����ͤˤ����� + * doFileCopy(), doFileSave() �� sprintf ��Ȥä� msg[LINELEN] �ؤ������� + �� Str msg �� Sprintf() ���֤������� + * local.c �� dirBuffer() �� sprintf ��Ȥä� fbuf[1024] �ؤ������� + �� Str fbuf ���֤������� + + +2000/12/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + maximum_table_width �� td, th ������ width °���ͤ��θ����褦�� + �ѹ�. + + +2000/12/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01473] Re: internal tag and attribute check + ������[w3m-dev 01446] �ǡ� + >> frame �����ɲä����°�� framename, referer, charset �ʤɤ� + >> ����ʤ��ΤǤ��礦��������Ū�˰��Ѥ�����ϻפ��դ��ޤ��� + �Ƚޤ�������<form charset=e> ���� w3m ����λ���Ƥ��ޤ��ޤ��� + accept-charset ��Ʊ�ͤǤ��Τǽ������ޤ����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��table ������ hborder °�����̾�Ǥ�����դ���褦���ѹ�. + ��table ������ border °�����ͤ�Ϳ�����Ƥ��ʤ��Ȥ��ΰ������� + ����. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01478] Option Setting Panel + ��Ĺ�Υ�����ɥ��� Option Setting Panel ���ȡ��ֱ�Ӥ��� + �������б������Ť餤�Τǡ��֤�ͤ��ѥå��Ǥ��� + + +2000/12/7 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��parse_tag �� gethtmlcmd �ε�ǽ���������褦�ˤ���. + ���ǽ�� parse_tag ������������°��������դ��ʤ��褦�ˤ���. + �ޤ�, ����°�����ޤޤ�����, ��������°����ޤޤʤ��褦�˥��� + ����ľ���褦�ˤ���. + ��visible_length �Ǥ����פʥ����β��Ϥ�ߤ. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01456] linein.c + m17n ����� feed back �Ǥ�����linein.c �� calcPosition() �١����� + ��ľ���ޤ����������� display.c �Ȥۤ�Ʊ�ͤǤ��� + Ĺ��ʸ������˥��֤䥳��ȥ�����ʸ�������äƤ��������������뤬 + ư���褦�ˤʤäƤ���Ȼפ��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01457] cursor position on sumbit form + TAB������<input type="submit" �� value="OK">�ξ�˥���������ư�� + �����Ȥ��ΰ��֤�����Ƥ�������ؤ��н�. + + +2000/12/3 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01449] Re: Directory of private header of gc library. + popText (rpopText) �ǺǸ�����Ǥ���Ф�����ˤ��Υꥹ�Ȥ˥����� + �����褦�Ȥ���Ȱ۾ェλ���Ƥ��ޤ���������������Ф��뽤��. + + +2000/12/2 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ޤ�, image map ���Ȥ��ʤ����꤬�ĤäƤ����Τǽ���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �����ơ��֥� (MYCTYPE_MAP) �ˤ�ä�, ʸ����ʬ�ह��褦���ѹ�. + ����, latin1, ascii, internal character ��Ƚ�̤ˤ� INTCTYPE_MAP �� + �Ȥ��褦���ѹ�. + # ��̤Ȥ���ɬ��̵���ʤä� CTYPE_MAP �Ϻ������. + + +2000/12/1 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: Security hole in w3m (<input_alt type=file>) + ��HTMLlineproc1, HTMLtagproc1 ���ΰ����˥ե饰��������ơ� + ������������������Ȥ��ʤ��ͤˤ����� + ��map.c �� `<', `>' �����������Ȥ���Ƥ��ʤ��ä���ν����� +Subject: [w3m-dev 01432] Re: w3m-0.1.11-pre-kokb22 patch + �ޤ���������ȴ���꤬����ޤ�����patch ���դ��ޤ��� + ([w3m-dev 01431] �Ǥβ��Ĥ���λ�Ŧ�ؤν��������äƤޤ�) +Subject: [w3m-dev 01437] Re: w3m-0.1.11-pre-kokb22 patch + �������ƥ���Ϣ�ν����� image map ���Ȥ��ʤ��ʤ�����ؤ��н�. + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01415] Lineedit patch for kokb21 + Subject: [w3m-dev 00976] move & delete until /, &, or ? + ����Ƥ���URL�����ϵ�ǽ���ĥ����ѥå���w3m-0.1.11-pre-kokb21�Ѥ� + ��ľ���ޤ�����kokb20�Ǥ�ѥå�����������Ƥ��ޤ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���Ĥ���Υѥå� [w3m-dev 01427] �ͤ�, HTML �Хåե���ʸ���ζ� + ��������륳��ѥ��륪�ץ���� (ENABLE_REMOVE_TRAILINGSPACES) �� + �ɲä���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00301] Re: "w3m -h" outputs to stderr + w3m -h �ν������ stderr ���� stdout ���ѹ�. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01430] Re: w3m-0.1.11-pre-kokb22 patch + EWS4800(/usr/abiccs/bin/cc) �Υ���ѥ��륨�顼�ؤ��н�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��dumm_table ������ id °�����ϰϥ����å���ä���. + ��form_int ������ fid °�����ϰϥ����å���ä���. + ��table �����å��Υ����Хե����Υ����å���ä���. + + +2000/11/29 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01422] bpcmp in anchor.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01423] Re: bpcmp in anchor.c + ��®���Τ���δ��Ĥ��ν���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��checkType �ΥХ��ե���������Ӽ㴳�ι�®��. + �������� 2 �Х���ñ�̤ǰ����褦���ѹ�. + + +2000/11/28 + +From: Takenobu Sugiyama <sugiyama@ae.advantest.co.jp> +Subject: patch for cygwin + cygwin �Ǥ� ftp�����Ȥ���� download �Ǥ���, �ʲ��� patch���н�� + ���ޤ���. cygwin �Ǥ�, �ե������ open/close�� binary �⡼�ɤˤ� + �Ƥ����ʤ���, �������������꤬����褦�Ǥ�. + + +2000/11/27 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01401] Re: bugfix of display of control chars, merge of easy UTF-8 patch, etc. + ���ν������ɲäǤ�������ԤκǸ�˥���ȥ�����ʸ��������Ȳ��̥��� + ���̤���ʤ��ʤäơ�����ʸ����ɽ���Ǥ��ʤ��Х��ν����Ǥ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table �Υ�����ι�®��. + + +2000/11/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table �Υ�������� width °���ǻ��ꤷ����Τ�꾮�����ʤ�������� + ����ν���. + + +2000/11/24 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ޤ��ե�������ɤ߹���Ǥʤ��Ȥ���, �ץ����쥹�ѡ���ž��®�٤�ɽ�� + ���ʤ��褦���ѹ�����. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01385] Re: w3m-0.1.11-pre-kokb20 patch + w3m-0111-utf8-kokb20 �Ǥ�����conv.c �ǰ�ս�ְ㤤�Ȼפ���Ȥ��� + ������ޤ����Τǡ��ѥå���ź�դ��ޤ����Ĥ��Ǥˡ�����ǥ�Ȥ䥳��� + ������� warning �ν�����������Ƥ���ޤ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �����ޥ�ɥ饤��ǥ��ץ����������ѹ������Ȥ�, proxy �� cookie �� + �ѹ���ȿ�Ǥ���ʤ���ʬ�����ä�������Ф��뽤��. + ����������ե�������֤���Ȥ�, ���Υե���������Ƥ��ޤ� + ��������������Ф��뽤��. + + +2000/11/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��StrStream ���Ф��Ƥ�, ���� Str �ΤޤޥХåե��Ȥ������Ѥ���� + �����ѹ�. + ��get_ctype ��ޥ�������, �ơ��֥��Ȥä�Ƚ�Ǥ���褦�ˤ���. + ��menu.c ���֤��ͤ�����Ȱ��פ��Ƥ��ʤ��꤬���ä��Τǽ���. + + +2000/11/22 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��˹�®���Τ�����ѹ��Ǥ�. + ���ե������ɤ߹����˼����ǥХåե����Ԥʤ��褦�ˤ���. + ��conv.c �δؿ��� Str �١������ѹ�. + ��ǽ�ʸ¤�ʸ����Υ��ԡ���Ԥʤ�ʤ��褦�ˤ���. + ��checkType �ι�®��. + ������������ʸ����̵���Ȥ� cursorRight ��ư������꤬���ä��Τ�, + ��������. + �ޤ���Ԥ� LINELEN ��ۤ����Ȥ���, calcPosition ������γ��� + ���������ǽ��������Τǥ��������ѹ�. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01372] w3m sometimes uses the wrong mailcap entry + http://bugs.debian.org/77679 + �Ǥ�����mime type ��Ƚ�Ǥ� substring match �ˤʤäƤ뤫����� + �פ��ޤ��������ľ���ʤ��Ǥ��礦�� + + +2000/11/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��Ȥ�̵�� table �� table ����ˤ���Ȥ���, ���� table ��������� + ��ؤ��н�. + + +2000/11/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + gc6 �б�. + + +2000/11/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���Хåե������ζ���ʸ���� 0x80-0x9f �˳����Ƥ�褦���ѹ�����. + �����ܸ��ǤǤ�, �Хåե���Ǥ� �� 0xa0 ��ɽ�魯�ˤ���. + �����ܤ���δʰ� UTF-8 �Ǥ� UTF-8 �Ȥϴط�̵����ʬ�Υ����ɤ�ޡ��� + ����. + �ޤ��ǥХå��ΤȤ��������ʤΤ�, ���������ɤ�ʸ���ɤ˻��ꤹ�� + �����Ǥ���褦�ˤ���. + ��ɽ���Բ�ǽ�ΰ� (0x80-0xa0) �ˤ���ʸ���� \xxx �η���ɽ������褦 + �ˤ���. + ��Ϣ����, ���̥��եȻ���, ����ȥ�����ʸ�����ޤޤ�Ƥ����ɽ���� + ����Х������ä��Τǽ�������. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01354] minimize when #undef USE_GOPHER or USE_NNTP + #undef USE_GOPHER �� #undef USE_NNTP �Ȥ����Ȥ��ˡ���Ϣ���륳���ɤ��� + ����������ʤ��ʤ�褦���ѹ����Ƥߤޤ����� + + +2000/11/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �۾�ʼ��λ��Ȥ� getescapechar ���Ѥ��ͤ��֤�������������ؤ��н�. + + +2000/11/15 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��table ���Ȥ�������������Х��ν���. + ��DEL ʸ�����ޤ��֤���ǽ�ʶ���ʸ���Ȥ��ư����褦���ѹ���, �Хåե� + �����ζ���ʸ���� ���� DEL ���ѹ�. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01338] Re: Lynx patch for character encoding in form +Subject: [w3m-dev 01342] Re: Lynx patch for character encoding in form + form ������ accept-charset °��������դ���褦�ˤʤä�. + + +2000/11/14 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���������Ȥ���Τ�˺��Ƥ���Ȼפ�����ʬ����. + ��cleanup_str, htmlquote_str ��, �⤷ (����) �������Ȥ���ɬ�פ�̵ + �����, ����ʸ����Τޤ��֤��褦�ˤ���. + + +2000/11/10 + +From: ���Ƿ <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01336] patch for Cygwin 1.1.x + Cygwin 1.1.x (�����餯 1.1.3 �ʹ�) �����Υѥå���������ޤ����� + Cygwin 1.x �ʹߤδĶ��ˤ����ơ� + ��ɸ��Υ��ȡ���ѥ��� /cygnus/cygwin-b20/H-i586-cygwin32 + �ʲ����ѹ����ʤ� + ��T_as,T_ae,T_ac ����ˤ���Τ�� + + +2000/11/8 + +From: Jan Nieuwenhuizen <janneke@gnu.org> +Subject: [w3m-dev-en 00189] [PATCH] w3m menu <select> search + Enable to search within popup menu. + + +2000/11/7 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01331] Re: form TEXT: + ����ʸ����ȥե���������ʸ����Υҥ��ȥ�ΰ��ܲ�. + + +2000/11/4 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ����������������ۤ���Ȥ�, �������Ȥϲ���������������褦�ˤ���. + + +2000/11/2 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01313] Re: SCM_NNTP + MARK_URL �� nntp: ��ޥå�����褦�ˤ��Ƥߤޤ���������ɽ���� gopher: + �Τ�Τԡ����������Ǥ��� + + +2000/10/31 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01310] Re: option select (Re: w3mmee-0.1.11p10) + gc�饤�֥��Υ��顼��å�������disp_message_nsec���̤��ƽ��Ϥ��� + �褦�ˤ���. + + +2000/10/30 + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01294] mouse no effect on blank page. + mouse�����w3m ��blank�ʥڥ�����ɽ�����Ƥ������mouse�ܥ��� + �������ʤ�(��ܥ�������ʤ��Τ��ĥ饤)�Τǽ������Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01295] Re: mouse no effect on blank page. + �ºݤ�������櫓�ǤϤʤ��ΤǤ������������Ƥ������������Ǥ��͡� + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 01297] Re: backword search bug report + [w3m-dev 01296] ����𤵤�Ƥ���, ����������Ф����н�. + > �������ʤ�Ǥ�����"aaaa" �� "��������" �Τ褦��Ʊ��ʸ����Ϣ³���Ƥ� + > ��Ȥ��� "a" �� "��" �� backword search ����ȡ�����������֤� 1 ʸ�� + > ����Ǥ��ޤ��褦�Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01298] Re: backword search bug report + backword search �� wrapped search ��ͭ���λ������ߤιԤθ����� + �Ǥ��ʤ��Х���ľ���ޤ����� +Subject: [w3m-dev 01299] Re: backword search bug report + ���ܸ������Ȥ��� 2byte�ܤȼ���ʸ���� 1byte�ܤȤǥޥå������� + ��ȡ� little endian �Ǥ�����ɽ�� [��-��] ����������ǽ���ʤ����ꡢ + �Ѹ��ǤǤ� latin1 ����꤯�����Ǥ��ʤ��ä�(�Ǥ�����)�����ľ���ޤ����� + + +2000/10/29 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��LESSOPEN ����Ѥ��뤫�ɤ����� Option Setting Panel ������� + ���ˤ��� (default �ϻ��Ѥ��ʤ�). + �����̥ե����뿭ĥ��Υƥ�ݥ��ե��������Ȥ��γ�ĥ�Ҥ�, ���� + �ե�����γ�ĥ�� (.Z, .gz, .bz2) ���������ʬ�������褦���� + ������. + ��gunzip_stream, save2tmp, visible_length �ι�®��. + + +2000/10/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ����ѥ����, �ե�����̾�䴰��Υ��������Ƥ� Emacs-like �ˤǤ���� + ���ˤ���. + (config.h �� #define EMACS_LIKE_LINEEDIT �ˤ��ޤ�) + �ޤ�, �䴰����������˥Хå�������������ǽ�ˤ���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01284] Re: improvement of filename input + ��URL���ϻ�(U)�Ǥ� file:/ ����Ϥ����Τߥե�����̾�䴰��ͭ�� + �ˤ��ޤ����� + (URL ���Ϥλ��;夳��ʳ��Ǥϳμ¤� local-file �ˤʤ�ʤ�����) + ����������Υ��ɥХ����ˤ�� CTRL-D �Ǥΰ���ɽ���ϡ� + ʸ����κǸ�˥������뤬������˸��ꤷ�ޤ����� + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01280] Stop to prepend rc_dir to full path. + rcFile()�ե�ѥ��ˤ�rc_dir���դ��ʤ��褦����ѥå��Ǥ��� + + +2000/10/27 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01269] Re: SCM_NNTP + [w3m-dev 1258] �Ǻ��ܤ���Ŧ����Ƥ����Ȥ����������Ƥߤޤ������ѥ� + ����ź�դ��ޤ�����δĶ��Ǥϡ����ν����ʤ��� news:<Message-ID> �� + ư���ޤ���Ǥ����� +Subject: [w3m-dev 01273] Re: SCM_NNTP + url.c �������ơ�#undef USE_GOPHER �� #undef USE_NNTP �ΤȤ��ˤ� + gopther: �� news: ��ư��ʤ��褦�ˤ��ޤ������ޤ���nntp: ��ư��ʤ� + �褦�ˤ��ޤ����� + �ä��ơ�GOTO URL �� mailto: �����Ϥ����Ȥ���ư���褦���ѹ����Ƥߤ� + �������Ĥ��Ǥˡ������Ȥδְ㤤��ľ���Ƥ���ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01258] improvement of filename input + �Dz��Ԥǥե�����̾�����Ϥ�����ζ�����Ԥ��ޤ����� + ��Ctrl-D ���䴰����ΰ�����ɽ������褦�ˤ��ޤ����� + ���̤������ڤ�ʤ�����Ϣ³���� Ctrl-D �Ǽ��θ���ΰ������Фޤ��� + # ʸ���κ���� BackSpace �� Del ��ȤäƤ��������� + ��URL ���ϻ�(GOTO)��ʸ���� file:/, file:/// �� file://localhost/ ���� + �ϤޤäƤ�����ϡ��ե�����̾���䴰�����ͤˤ��ޤ�����(���Ť������˾) + # http: �� ftp: �ϲ��⤷�ޤ��ҥ��ȥ꤫����䴰�Ǥ������ɡ� + ��URL ��ҥ��ȥ����¸������� password ��ʬ�Ϻ�������ͤ˽������ޤ����� + �ʤ����������餢�� undocument �ʵ�ǽ�Ǥ���������ʸ�������Ϥʤɤξ��Ǥ⡢ + Ctrl-X �� TAB(Ctrl-I) �Ǥ� �ե�����̾�䴰��ͭ���ˤʤ�ޤ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01277] Accept-Encoding: gzip (Re: some wishlists) + Accept-Encoding: gzip, compress + ��ꥯ�����ȥإå����դ���褦�ˤ���. +Subject: [w3m-dev 01275] Re: squeeze multiple blank lines option ( http://bugs.debian.org/75527 ) + �Ȥꤢ���� #ifdef DEBIAN �� + squeeze multiple blank line �� -s + ü��ʸ�������ɻ���� -s/-e/-j �ϥʥ��������� -o kanjicode={S,E,J} ��Ȥ� + ���Ȥˤ��Ƥ����ޤ��� +Subject: [w3m-dev 01274] Re: SCM_NNTP + ���ä����ʤΤ� nntp: �ݡ��Ȥ��Ƥߤޤ��� +Subject: [w3m-dev 01276] URL in w3m -v + LANG=EN (�Ȥ����� undef JP_CHARSET)�λ��� visual mode �ǻȤ��Ƥ� URL + ���������ʤ��褦�Ǥ��� + + +2000/10/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + mailcap �� mime.type �ե�����ξ��� Option Setting Panel ������ + ��ǽ�ˤ���. + + +2000/10/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01247] Re: buffer selection menu + ��˥塼��Ϣ�� patch ����ӻ����ѹ� [w3m-dev 01227], [w3m-dev 01228], + [w3m-dev 01229], [w3m-dev 01237], [w3m-dev 01238] ��ޤȤ�ޤ����� + ��Select ��˥塼�Ǥξõ�(������ 'D') + ��Select ��˥塼�ǤΥ����Ȥ�ɽ�� + ��--- SPC for select / D for delete buffer ---�� + ������������������������������������������������ + ����˥塼����Υ��ޥ�ɼ¹Ԥ���ġ� + + +2000/10/24 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� ���å����������, `.' �����ƤΥɥᥤ���ɽ�魯�褦�ˤ���. + �� bm2menu.pl �� CVS �� add ����Τ�˺��Ƥ����Τ�, �ɲ�. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01240] Re: w3m-0.1.11-pre-kokb17 patch + �Ȥꤢ��������ѥ������ incompatible pointer type �Ȥ���줿�Ȥ� + ���ν����ѥå���ź�դ��ޤ��� + + +2000/10/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� ���ץ��������ѥͥ��, ���å���������դ��� (�����դ��ʤ�) �� + �ᥤ�������Ǥ���褦�ˤ���. + �ޤ�, ���å���������ĤΥ��������Ȥ���ʬΥ����. + �� frame �� reload �κ�, �ץ������Υ���å��夬��������Ƥ��ʤ��� + ������ؤ��н�. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01211] Re: a small change to linein.c +Subject: [w3m-dev 01214] Re: a small change to linein.c + Ĺ��ʸ������Խ������, ���Ƥ�ʸ����ɽ������ʤ�������������ؤ� + �н�. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01216] error message for invalid keymap +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01220] Re: error message for invalid keymap + keymap �����꤬���ä��Ȥ���, ���顼��å�������Ф��褦�˽���. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01217] keymap.lynx example could be better. + keymap.lynx �ι���. + + +2000/10/20 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + cookie �μ�갷���˴ؤ��ƴ��Ĥ��ν�����ä���. + �� version 1 cookie ���Ф��밷���� + http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt + �˽��褦���ѹ�. + Netscape-style cookie �Υꥯ�����ȥإå���, Cookie2 ���ɲ�. + �� [w3m-dev-en 00190] patch ���Ф�����Ĥ����ѹ�. + + +2000/10/19 + +From: "Ambrose Li [EDP]" <acli@mingpaoxpress.com> +Subject: [w3m-dev-en 00136] version 0 cookies and some odds and ends +Subject: [w3m-dev-en 00191] sorry, the last patch was not made properly +Subject: [w3m-dev-en 00190] w3m-0.1.10 patch (mostly version 0 cookie handling) + I've hacked up a big mess (patch) against w3m-0.1.9 primarily + involving version 0 cookies. To my dismay, it seems that most + servers out there still want version 0 cookies and version 0 + cookie handling behaviour, and w3m's cookie handling is too + strict for version 0, causing some sites (notably my.yahoo.co.jp) + not to work. + +2000/10/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ʸ�����������ǽ�ˤ���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01208] '#', '?' in ftp:/.... + ftp:/ �ǥե�����̾�� '#' �����äƤ���ȥ��������Ǥ��ʤ�����ؤ��� + ��. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01209] http_response_code and ``Location:'' header + ��Location:�ץإå�������ȡ�̵���ˤ���˽����褦�ˤʤäƤޤ����� + http_response_code��301��303�λ����������褦�ˤ��Ƥߤޤ����� + + +2000/10/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + local CGI ��, ����Ӥ��Ǥ�������ؤ��н�. + + +2000/10/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table ��� <textarea> ���Ĥ��Ƥʤ���, ��λ�Ǥ��ʤ��ʤ�����ؤ��н�. + ([w3m-dev 00959] �����ذ�). + <select> �ΰ����˽स��褦�ˤ���. + +From: maeda@tokyo.pm.org +Subject: [w3m-dev 00990] auth password input + �����Ф���ѥ���ɤʤΤ��狼��ʤ��Τǡ��ʲ��Τ褦�� + �ѥå������Ƥޤ�����sleep(2)��Ĺ�����뤫�⡣ + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01193] Re: frame bug? + �ե졼��Τ���ڡ�������褷�Ƥ���Ȥ�, ����������������ؤ��н�. + + +2000/10/13 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00928] misdetection of IPv6 support on CYGWIN 1.1.2 + CYGWIN 1.1.2�ʹߤ�, ���ä� IPv6 ���ݡ��ȤФ��Ƥ��ޤ�����ؤ��� + ��. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01170] Re: cursor position after RELOAD, EDIT + ��cache �ե����뤬�Ĥ뤳�Ȥ�����Х��ν���. + ����¾ + ���ǥ��쥯�ȥ�ꥹ�Ȥ� URL �� /$LIB/dirlis.cgi�� �ȳʹ������ä��Τǡ� + ���Υǥ��쥯�ȥꤽ�Τ�Τˤʤ�褦�ˤ��ޤ����� + dirlist.in ���ѹ����Ƥ��ޤ��Τǡ�configure ��Ƽ¹Ԥ��뤫�� + cp dirlist.in dirlist.cgi �Ȥ��� @PERL@ �� @CYGWIN@ ������Ƥ��������� + ��keymap �ǰ����ҤǤ����ĥ��ʲ��δؿ���Ŭ�Ѥ��ޤ����� + LOAD �� �ե�����̾ + EXTERN, EXTERN_LINK �� �����֥饦��̾ + (w3m-control: ����ϻȤ��ޤ���) + EXEC_SHELL, READ_SHELL, PIPE_SHELL �� shell���ޥ�� + (w3m-control: ����ϻȤ��ޤ���) + SAVE, SAVE_IMAGE, SAVE_LINK, SAVE_SCREEN �� �ե�����̾(pipe ���ޥ��) + (w3m-control: ����ϻȤ��ޤ���) + + +2000/10/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��ɸ�����Ϥ���ΥХåե����ɤ߹���Ȥ�, MAN_PN �ƥХåե�̾���� + ��褦�ˤ���. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01156] Re: w3m-0.1.11-pre-kokb15 + ��mydirname �ΥХ������ȴؿ�������ɲ� + ��SERVER_NAME �����ꤹ��褦���ѹ� + ��[w3m-dev-en 00234] �ͤ� GATEWAY_INTERFACE �����ꤹ��褦���ѹ� + ��current working directory ���ѹ����� popen ���롢���ޤȤ�ʼ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01158] some bugs fix when RELOAD, EDIT +Subject: [w3m-dev 01164] cursor position after RELOAD, EDIT + ��local CGI �Ȥ��ƸƤӽФ��� file:... �� EDIT �Ǥ���Х��������ޤ����� + # currentURL.scheme �ǤϤʤ� real_scheme ��Ȥ��褦�ˤ��ޤ����� + ��HTML ����ɽ�����֤��� RELOAD, EDIT ������ˤ� + ������ɽ�����֤ˤʤ�褦�ˤ��ޤ���(�����Զ�礬����ޤ���)�� + ���դ� plain text �ե������ HTML ɽ�����Ƥ�����֤��� RELOAD, EDIT + ������ˤ� HTML ɽ�����֤ˤʤ�褦�ˤ��ޤ����� + ��RELOAD, EDIT ��Υ���������֤� RELOAD, EDIT ����Ʊ���ˤʤ�褦�� + ���ޤ����� + + +2000/10/10 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01166] Re: cell width in table + table �ط��ΥХ��ե������Ǥ�. + �� ����������ʬ����ˤ�ؤ�餺, ʸ��������ޤ��֤���Ƥ��ޤ�����ν���. + �� table �� <wbr> �������ʤ�������������ν���. + �� feed_table_tag() �ν����ζ��̲�. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01155] history of data for <input type=text> + �դȻפ��Ф��� <input type=text> �����Ϥ����ǡ�����ҥ��ȥ�� + é����ͤˤ��Ƥߤޤ����� + ���������ӥ����Ϥ��⤯���ʤɤ������Ȼפ��ޤ��� + + +2000/10/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01150] Some bug fixes + [w3m-dev 00956] unknown scheme in frame + [w3m-dev 00975] goto link from frame page + ����𤵤줿����ν���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01145] buffer overflow in linein.c + inputLineHist(linein.c) �ǥǥե����ʸ���� 256 ʸ���ʾ�ξ��� + strProp ���ΰ賰�����������뤳�Ȥ�����ޤ����Τǡ����ν��� patch �Ǥ��� + �ޤ�ʸ����Ĺ�������ͤ� 1024 �ˤ��ޤ����� + + +2000/10/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01136] function argument in keymap +Subject: [w3m-dev 01139] Re: function argument in keymap + Ĺ�餯����ˤʤäƤ� ~/.w3m/keymap �Ǥδؿ��ΰ���������ǽ�ˤ��ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01143] image map with popup menu + image map �� popup menu ��Ȥä� <option> ���ͤ�ɽ������褦�ˤ��Ƥߤޤ����� + config.h �� #define MENU_MAP �Ȥ��ƥ���ѥ��뤷�ƤߤƤ��������� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 00971] Re: segmentation fault with http: + URL �Ȥ��� http: �� http:/ �����Ϥ��������Ƥ��ޤ��Τǽ������ޤ����� + + +2000/10/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01134] w3m in xterm horribly confused by Japanese in title (fr + http://bugs.debian.org/w3m ����𤵤�Ƥ���, �Ѹ��Ǥ����ܸ쥿���ȥ�� + ����ڡ������Ȥ���, w3m ��ȯ�������������������Ф���Х��ե����� + �Ǥ�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01127] SIGINT signal in ftp session (Re: my w3m support page) + ftp �κݤ� SIGINT ��ȯ������������Х��ν���. + + +2000/10/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� table �� recalc_width() �� wmin �κ����ͤ� 0.05 ���ѹ�. + �� �������ޥ�ɤν��ϥХåե��� filename, basename, type ���ѹ�. + �� http �� local file �ʳ��ΰ��̥ǡ�����Ĺ����Τ�, ��ö�ƥ�ݥ�� + �ե��������Ȥ��褦�ˤ���. + �� �ƥ�ݥ��ե�����̾������������ˡ���ѹ�. + �� mailcap �� edit= ���᤹��褦�ˤ���. + �� URLFile �ν�������Դ������ä�����ν���. + �� �ĤäƤ���������ѥå��Υ��ߤκ��. + + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + -dump, -source_dump ���ץ����β���, frame ��� <meta> ������̵�� + ����褦�ˤ�. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00930] HTML-quote in w3mbookmark.c + "�֥å��ޡ�������Ͽ" �� URL �� Title �� HTML-quote ����Ƥ��ʤ��Τ� + �������ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00972] better display of progress bar ? + 2Mb �Υե�������ɤ�Ǥ�����ˡ����ä� 0/2Mb �ˤʤä��ᤷ���ä��Τǡ� + �ץ����쥹�С���ɽ���� %.0f (%.1f) ���� %.3g �ˤ��Ƥߤ���Ǥ����� + �ɤ�ʤ��Ǥ��礦�� + + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + textlist ���Ф��� null pointer �����å���ä���. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01100] space in URL + + * http://bugs.debian.org/60825 �� http://bugs.debian.org/67466 + + form �� submit ������� value ���� form_quote() ���Ƥޤ��� + name ������ form_quote() ����ɬ�פ�����ޤ��� + + * http://bugs.debian.org/66887 + + Goto URL: ����Ƭ�� space ������� current��������а����ˤʤ�Τ� + ���Ƥۤ����Ȥ�����𡣤������� cut & paste ����Ȥ��ˤʤ꤬���ʤΤ� + (�Ĥ��ǤʤΤǸ���ζ������) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01111] bug of conv.c + UTF-8 �ʥڡ���(Shift_JIS �ȸ�ǧ�����)�� w3m ��ɽ�������� + (����ȥ����륷������ϳ���)���Ȥ����ä��Τ�Ĵ�٤Ƥߤ��Ȥ����� + conv.c ���Х��äƤޤ�����ñ��ߥ��Ǥ������ߤޤ���m(_o_)m + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01113] bug fix (content charset) + content charset ���������ǥХ��äƤޤ����Τǡ����� patch �Ǥ��� + + +2000/10/02 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01112] Re: mailcap test= directive + mailcap �ΰ������ĥ���ޤ���. + �� %s �ʳ���, %t (content-type name) ��Ȥ���褦�ˤ��ޤ���. + �� nametemplate ���ץ����ͭ���ˤʤ�ޤ���. + �� %s ��̵������, ɸ�����Ϥ� %s �˥�����쥯�Ȥ��ƥ��ޥ�ɤ�¹Ԥ� + ��褦�ˤ��ޤ���. + ������ι�ʸ�Ȥ��ƥܡ�������ꤷ�Ƥ���Τ�, OS/2 ���ǤϤ��� + �ޤޤǤ����ܤ��⤷��ޤ���. + �� needsterminal �����ꤵ��Ƥ������, �ե��������ɤǥ��ޥ�ɤ�� + �Ԥ���褦�ˤ��ޤ���. + �� copiousoutput �����ꤵ��Ƥ������, ���ޥ�ɤμ¹Է�̤�Хåե� + ���ɤ߹���褦�ˤ��ޤ���. + �� RFC 1524 �ˤ�̵���ΤǤ���, ���ޥ�ɤμ¹Է�̤� text/html �Ȥ��� + �Хåե����ɤ߹��ि��Υ��ץ���� htmloutput ���ɲä��ޤ���. + �����, ���ܤ��� [w3m-dev 01079] ����Ƥ���Ƥ�����Τ����ذƤ� + �Ĥ��Ǥ�. + �ޤ��ƥ��Ȥ��Ƥޤ���, ������ư���Ƥ���� + + application/excel; xlHtml %s | lv -Iu8 -Oej; htmloutput + + �Ȥ����, lv �μ¹Է�̤� html �Ȥ��� w3m �ΥХåե���ɽ������� + �Ϥ��Ǥ�. + Ʊ�� content-type �Υ���ȥ꤬ʣ��������, htmloutput ���ץ���� + �������Τ�ͥ�褹��褦�ˤ��Ƥ���Τ�, ¾�Υץ������� mailcap + ��ͭ���Ƥ�����̵���Ȼפ��ޤ�. + ������, RFC 1524 �˽�Ƥʤ��ΤϳΤ��ʤΤ�, ��ո����Ԥ����Ƥ� + ��. + �� (gunzip_stream() �ˤ��) ���̥ե�����α����� ftp ���Ф��Ƥ�Ȥ� + ��褦�ˤ��ޤ���. + ¿ʬ [w3m-dev 01078] �ΥХ����Ȼפ��ޤ���, http ���Ф���, ���̤� + ���ƥ����ȥǡ����α������Ǥ��ʤ��ʤäƤ��Τ�, �������ޤ���. + + +2000/09/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01097] gunzip_stream problem + ���̥ե�������ɤ߹���Ǥ������, INT �����ʥ뤬ȯ�������Ȥ���ư�� + ���ѤʤΤ�, �������ޤ���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01092] CONFIG_FILE + config.h �� CONFIG_FILE ���ѹ����Ƥ�ȿ�Ǥ���ʤ��ʤȻפä��顢 + ���ĤΤޤˤ��ϡ��ɥ����ǥ�����äƤޤ����� + ���ν����Ǥ��� + + +2000/09/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01078] treatment of content type + document type �ΰ����β��ɤ�Ԥʤ��ޤ���. + �� examineFile �ˤ�����, lessopen_stream �� gunzip_stream ��ͥ���� + ���ѹ����ޤ���. + �� lessopen_stream �ν������, plain text �Ȥ��ư����褦�ˤ��ޤ���. + �� lessopen_stream ��, document type �� text/* �Ǥ��뤫, �����ӥ塼�� + �����ꤵ��Ƥ��ʤ����ΤȤ��褦�ˤ��ޤ���. + �ޤ�, text/html �ʳ���, text/* ���� w3m �����ǽ�������褦�ˤ��� + ����. + �� page_info_panel ��ɽ������� document type ��, examineFile �ǽ��� + ����������ͤ�Ȥ��褦�ˤ��ޤ���. + �� �����ӥ塼����Хå������ɤ�ư�����Ȥ�, ���ޥ�ɥ饤��� + ">/dev/null 2>&1 &" ���դ��Ƥߤޤ���. + + +2000/09/13 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01053] Re: Location: in local cgi. + [w3m-dev 01051] �Υѥå��Ǥϡ�w3m -m �� Location: �Υإå��Τ���ʸ�Ϥ� + ���������Ǥ��äƤ��ޤ��Τǡ�local CGI �ΤȤ��Τ� Location: �Ȥ� + ��褦���ѹ����ޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01065] map key '0' + keymap ����ߤν����Ǥ��� + ��ñ�Ȥ� '0' ���ޥåײ�ǽ�ˤ��ޤ����� + ��10 j�٤Ȥ��ϰ����̤�Ǥ��� + ����ESC ���٤ʤ� ESC �θ�� 0x80-0xff ��ʸ�������Ϥ���� + �������������ǽ�������ä���Τ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01066] 104japan + frame ��� form ��ʸ�������ɤ��Ѵ�����꤯�����Ǥ��Ƥ��ʤ��褦 + �Ǥ��Τǡ��������ޤ����� + + +2000/09/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01058] <dt>, <dd>, <blockquote> (Re: <ol> etc.) + �� <blockquote> ������ζ��ԤϾ������褦�ˤ���. + �� <dt>, <dd> ����ľ��� <p> ������̵�뤷�ʤ��褦�ˤ���. + + +2000/09/04 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01052] cellpadding, cellspacing, vspace, etc. + �������Ԥ˴ؤ���, ���Τ褦�ʤ����Ĥ����ѹ���Ԥʤ��ޤ���. + �� ;ʬ�ʥ��뤬�����Τ��ɤ������, <tr> �� <td> �γ��ˤ��� + <a name="..."></a> ��, <font> ���ϼ��Υ��������褦�ˤ���. + �� <table> �� cellspacing °���β���ְ�äƤ����Τ�, ��������. + vspace °������Ǥ���褦�ˤ���. + �� ���Ԥ�Ƚ������ѹ�����. + �� </p> �����Ƕ��Ԥ�����褦�ˤ���. + + +2000/08/17 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01018] sqrt DOMAIN error in table.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01019] Re: sqrt DOMAIN error in table.c + �������Ȥ���ˤʤ��礬��������ν���. + + +2000/08/15 + +From: satodai@dog.intcul.tohoku.ac.jp (Dai Sato) +Subject: [w3m-dev 01017] value of input tag in option panel + aito Ϣ��Ģ��http://ei5nazha.yz.yamagata-u.ac.jp/BBS/spool/log.html�� + �˽ФƤ�����Ǥ���option ���̤γ��� editor �ʤɤ� '"' ���ޤޤ�� + ���ޥ�ɤ����ꤵ���ȡ����� option ���̤�ƤӽФ������� '"' �ʹߤ� + ɽ������ʤ��ʤ�ȸ������ꡣ + + +2000/08/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01016] Table geometry calculation + table �Υ�����ȥ���Ǽ¿��������˴ݤ������ѹ�����, table ���� + �����ͤ����������κ�����ǽ�ʸ¤꾮�����ʤ�褦�ˤ��Ƥߤޤ���. + + +2000/07/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01006] initialize PRNG of openssl 0.9.5 or later + �С������ 0.9.5 �ʹߤ� openssl �饤�֥���, ������ǥХ��� + (/dev/urandom) ��¸�ߤ��ʤ��Ķ��Ǥ� SSL ���Ȥ���褦�ˤ��Ƥߤޤ���. + + +2000/07/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01004] unused socket is not closed. + C-c (SIGINT) �ǥե�������ɤ߹��ߤ����Ǥ����Ȥ�, socket �������������� + �Ƥ��ʤ���礬����褦�Ǥ�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01005] table caption problem + </caption> ��˺��Ƥ����Ȥ��� w3m ����λ���ʤ��ʤ����������ν���. + + +2000/07/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00966] ssl and proxy authorization + authorization ��ɬ�פȤ������ HTTP proxy �����Ф� SSL �ȥ�ͥ� + �����꤬���ä��Τǽ���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01003] Some bug fixes for table + table �Υ�����ȥ���Τ����Ĥ���������Ф��뽤��. + + +2000/07/16 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00999] Re: bookmark + �֥å��ޡ�������Ͽ�Ǥ��ʤ���礬��������ν���. + + +2000/06/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00934] clear_buffer bug + clear_buffer �� TRUE �ΤȤ�, selBuf() �Dz��̤��ä��Ƥ��ޤ�������Ф��� + �Х��ե������Ǥ�. + + +2000/06/17 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00929] ftp.c patch + USER ���ޥ�ɤ��Ф��� 230 ���֤äƤ������ˤ�����������Τ� + �ߤʤ� patch ��������ޤ������ʲ���ź�դ��ޤ��� + + +2000/06/16 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00923] some bug fixes + �� #undef JP_CHARSET �ξ��� file.c �� make �Ǥ��ʤ��ʤäƤ��� + �Х�(��Υߥ��Ǥ���_o_)�ν����ȡ� + �� buffer.c �� '=' �� '==' �ˤʤäƤ�����Τν����Ǥ��� + +From: Kazuhiko Izawa <izawa@nucef.tokai.jaeri.go.jp> +Subject: [w3m-dev 00924] Re: w3m-0.1.11pre +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00925] Re: w3m-0.1.11pre + file://localhost/foo �η����� URL �˥����������褦�Ȥ����Ȥ��۾ェ + λ���Ƥ��ޤ�����ν���. + +2000.6.14 +From: aito +�� ~/.w3m �������ʤ��ä��Ȥ��ˤϡ�cookie �� config ����¸���ʤ��褦�ˤ����� +��<isindex prompt="..." action="...">���б��� +��<tag/>�����ϤǤ���褦���ѹ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +��[w3m-dev 00846] doc-jp/w3m.1 �η����� mandoc ���� man ���Ѵ��� +��[w3m-dev 00861] ɸ�����Ϥ���ǡ������ɤ�Ǥ���Ȥ��ˡ�'G' �� + ��ư�����������Х��ν����� +��[w3m-dev 00874] FTP_proxy == "" �ξ��������Х��ν����� +��[w3m-dev 00875] uncompress, gunzip ��ʬ�Υ����ɤǵ��ˤʤä���ʬ���� + ���ޤ����� +��[w3m-dev 00876] �Хåե��˰��̥ե������ɽ�����Ƥ���Ȥ������ΥХåե��� + ��ɽ�������ʸ����������Х��ν����� +��[w3m-dev 00887] getNextPage() ����ߤν���/���ɤޤ����� + ��-m ���ץ������ѻ��� quoted-printable ��ǥ����ɲ�ǽ�ˤ����� + ��showProgress �� getNextPage() ��ǸƤ��ͤˤ����� + ����ˤ�ꡢTransferred byte(buf->trbyte) ���������ͤ�����褦�ˤʤä��� + ���ѿ�̾�� loadBuffer ���Ȥ���������碌���� + �ޤ���getNextPage �Ȥϴط�̵����ʬ�Ǥ� + ��showProgress ��Ƥְ��֤��������Ȼפ�����֤��ѹ� + ���Ƥ��ޤ��� + + +From: satodai@dog.intcul.tohoku.ac.jp (Dai Sato) +��[w3m-dev 00848] NEWS-OS 4 �б��� + +From: Hiroshi Kawashima <kei@arch.sony.co.jp> +��[w3m-dev 00849][w3m-dev 00863] mipsel patch �ν����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> and many others +��[w3m-dev 00851] #ifdef JP_CHARSET ���դ�˺��ν����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +��[w3m-dev 00859] caption ���ޤ��֤���ʤ��Х��ν����� +��[w3m-dev 00872] <LI> �Ƕ��Ԥ�����Х��ν����� +��[w3m-dev 00891]���Τ褦��������Ф���Х��ե������Ǥ�. + 1. table ��� <pre><nobr>, <xmp>, <listing> �� table �������. + 2. <xmp>, <listing> ��ľ�夫����ԤޤǤ�̵�뤵��Ƥ���. + 3. table ��� <textarea>, <xmp>, <listing> ������˴ޤޤ�륿����� + �����Ƥ��ޤäƤ���. + 4. feed_select() �Υ����ޥå��� <option> �ϻϤ�� 7 ʸ���������� + ���Ƥʤ��ä�����, <optionxxx> ���Υ����ˤ�ޥå����Ƥ�. + �դ�, </option> �� > �����ζ�������Ƥʤ��ä�. + 5. <table> ��� </script> (</style>?) ��˺�줿�Ȥ���, ̵�¥롼�פˤ� + ��. + ����˼��Τ褦���ѹ��ޤ���. + 6. goLine �ǥ������뤬��Ƭ�˹Ԥ��褦�ˤ���. +��[w3m-dev 00914] �����Ƥ��ʤ��Хåե��Υ�����������Ȥ��ˡ� + �������Ƥ�å���Ȥ��ƥե�����˳�Ǽ����褦���ɡ� + + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��[w3m-dev 00853] dirlist.in �ν����� + +From: Altair <NBG01720@nifty.ne.jp> +��[w3m-dev 00885][w3m-dev 00892] for OS/2 + ��Netscape��Lynx�Υ֥å��ޡ������顢�������롦�ե�����˥��������� + ���ʤ��ä��Τ���(������ �礵��Υѥå�)��file:///D|path/file�� + ��(D�ϥɥ饤�֥쥿��)�ݡ��ȡ� + ����Ȱ�Ȥ��ơ�/tmp�ɥǥ��쥯�ȥ�����Ǥ����Ƥ����ΤĶ��ѿ� + TMP�������ͥ�衣(����������� �礵��Υѥå�)�� + ��DOS��OS/2�Υ�����ǹ����Ȥ��Ƥ���IBM codepage 850ʸ������ + �ˡ�ISO latin-1������Ѵ���Ԥ����Ѹ���w3m�Ǥ�ʸ��������ʤ����� + ������ + ��Insert�����ˤ���˥塼�ƤӽФ���OS/2�Ķ��Ǥ��ǽ�ˡ� + ��xterm��kterm��rxvt�ʤɤǡ����̤˥��ߤ�ɽ������Ƥ��ޤ��Τ��к��� + (XFree86/OS2�ǡ������ߥʥ��IEXTEN�ե饰�Υ���ץ���Ȥ�Linux + �ʤɤȰ㤦�Τ�����)�� + ���������Ͻ�������Υѥå����������ˤ�ѥå��Ǥ������ޤ��ȸ��ä�ʷ + �ϵ����ä��Τ��ꥸ�ʥ�Υץ������˻������� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +��[w3m-dev 00898][w3m-dev 00899] close_effect0 �� close_anchor0�ΥХ������� + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +��[w3m-dev 00908] case-sensitive search�μ����� + + +2000.6.6 +From: aito +��[w3m-dev 00826] + ��CGI �� POST ��åɤǼ��������إå��� Location: �����ä���硢������ + redirect ���줿�ڡ����� reload ����ȼ��Ԥ���Х��ν����� + ��URL��ζ���ʸ����ä��������ɲá� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +��[w3m-dev 00827] onA() �������Ƥ��ʤ��Х��ν����� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��[w3m-dev 00835] frame��Υ�٥�ؤΰ�ưư��β��ɡ� + +2000.6.5 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +��[w3m-dev 00789] ���ʤ�Ť��Х��Ǥ���, <li> ���������������äƤ�褦�Ǥ�. +��[w3m-dev 00801] �Dz��Ԥ���뤳�Ȥ�����Х��ν����� +��[w3m-dev 00813] ʸ����� > �����ޤ����ϤǤ��ʤ�����ν����� +��[w3m-dev 00818][w3m-dev 00819] <textarea>���<xmp>,<listing>�������ޤ� + ư���Ƥ��ʤ��Х��ν����� +��[w3m-dev 00820] �����ǹԤ����äƤ���Ȥ�, �Ԥ�Ĺ���������� + 1 �Ǥ����� + ����, ������եȤ��ʤ���������褦�Ǥ�. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��[w3m-dev 00807] table���<select>�Τʤ�<option>���и����������� + �Х��ν����� +��[w3m-dev 00816] <textarea>��</textarea> ��β��Ԥ�������֤�����ä� + ���ޤ��褦�Ǥ��� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +��[w3m-dev 00814] 'V' ���ޥ�ɤ�text�Ǥʤ��ե��������ꤹ��ȡ����θ� + �����Х��ν����� + +2000.6.1 +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +��[w3m-dev 00578] HTTP_HOME ���� w3m -v ��Ƚ��ν�������� + �ؤ���ѥå��Ǥ��� +��[w3m-dev 00581] BUFINFO��Ϣ�ΥХ������� +��[w3m-dev 00641] config ��� extbrowser �����꤬ȿ�Ǥ���ʤ��Х��ν����� +��[w3m-dev 00660] �����ץ��������Ϥ��ѥ�̾����/home/okada/.w3m//w3mv6244-0..pdf + �Τ褦�ˤʤäƤ��ޤ��������ΤޤޤǤ�ư�������Ϥʤ��Ǥ������������Ƥߤޤ����� +��[w3m-dev 00672] configure ��� BUFINFO ������� +��[w3m-dev 00701] [w3m-dev 00684]�β��ɡ� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��[w3m-dev 00582] + ��w3m -T a -B ���ǥ�����(�����)�� SEGV ���ޤ��� + ��w3m -o 1 ���� SEGV ���ޤ��� + ����������ƥ��֤ˤʤ�ʤ�����ޤ��� + ��kterm �ǥޥ����������ޤ��� +��[w3m-dev 00584] + ��show_params() �� sections[j].name ������ conv() ���٤��Ȼפ��ޤ��� +��[w3m-dev 00586] + ��define CLEAR_BUF �ξ��˥Хåե�������̤ǡ� + Currentbuf �ʳ��ΥХåե��� [0 line] ��ɽ������Ƥ��ޤ��ޤ��� +��[w3m-dev 00605] + ��show_params() ��ɽ���β��ɡ� + ��define CLEAR_BUF �ξ��� HTML �ʳ��� local �ե����뤬 + reload ������ source �ե����뤬�ƥ�ݥ��ե������ + �Ѥ�äƤ��ޤ��ޤ�������� 2 ���ܤ� reload �����ȡ� + �ɤߤ�������Ʊ���ե�����˾���Ƥ��ޤ��ޤ��� +��[w3m-dev 00606] textarea �˺ǽ餫�餢�ä�ʸ�Ϥ��ѹ���������������ȡ����ԥ����� + �� CR �Τޤ��������Ƥ��ޤ��Х��ν����� +��[w3m-dev 00630] �ޥ�����ɥ�å����ƥ��������뤵��������Ф�˥��������뤵����� + ư����������ä��Τǽ������ޤ����� +��[w3m-dev 00654] [w3m-dev 00666] CLEAR_BUF ������ˡ�FORM�Τ���ڡ�����BACK������ + FORM�����Ƥ��ä���Х��ν����� +��[w3m-dev 00677] [w3m-dev 00704] ���ܸ쥳����Ƚ��β��ɡ� +��[w3m-dev 00684] ���ޥ�ɥ饤������Υ����å����������ޤ����� +��[w3m-dev 00687] save ����ư��ˤĤ��ưʲ��ν����Ȳ��ɤޤ����� + ��ftp �ΤȤ� Content-Type: application/? ���� download �ˤʤ���� + �ѥ��פؤν��Ϥ�����ʤ��ͤˤ����� + ��save ����ե�����̾�� URL ���������query ��ʬ�ϻȤ�ʤ��ͤˤ����� + ��URL ����ե�����̾�����ʤ��ä����ϡ�index.html ��Ȥ��褦�ˤ����� +��[w3m-dev 00696] + ��PIPE_SHELL('#') ���ɤ�����˥ѥ��פ��Ĥ����ʤ��ʤäƤ��ޤäƤ��� + ��READ_SHELL('@') �� PIPE_SHELL('#') �����ɤ߹�����Хåե��� + VIEW('v') �� HTML ɽ���Ǥ��ʤ��ä� + ��mouse ���ѻ��� EXEC_SHELL('!') �η�̤�ή��Ƥ��ޤäƤ��� +��[w3m-dev 00706] CLEAR_BUF ���ˡ����� : �ǥ����������Хåե���Ƥ� + ɽ������ȥ������ä��Ƥ���Х��ν����� +��[w3m-dev 00720] dirlist.cgi �������ơ��ĥ��¤�ξ��� + �����ǥ��쥯�ȥ�ΰ��֤������ͤˤ��ޤ����� +��[w3m-dev 00721] CLEAR_BUF �� ~/.w3m/config ���ѹ��Ǥ����ͤˤ��Ƥߤޤ����� +��[w3m-dev 00724] -m ���ץ������ѻ��˰�ĤΥإå���ʣ���Ԥ��Ϥ�� + ��꤯�����Ƥ��ʤ��Х��ν����� +��[w3m-dev 00728] HTTP�إå������ܸ줬���äƤ��������н衣 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��[w3m-dev 00589] w3m -T a -B ���ǥ�����(�����)�� SEGV ����Х��ν��� +��[w3m-dev 00595] frameset ��Ϣ�Х����� +��[w3m-dev 00610] frameset ��Ϣ�Х����� +��[w3m-dev 00631][w3m-dev 00633] ID_EXT��Ϣ�Х����� +��[w3m-dev 00632] <META HTTP-EQUIV="Refresh">��URL���character entity �� + ���ä����ΰ������ѹ��� +��[w3m-dev 00646] ����å��֥�ե����।����Τ��礣�ȼФ��å�����Τȡ� + table��frame��frame name��ID�����Ȥ���������patch��Ĥ��ޤ��� +��[w3m-dev 00680] +��[w3m-dev 00683] frame ��� <STRONG> �������ȤˤʤäƤ��ޤ��Х��ν����� +��[w3m-dev 00707] frame��Ϣ�ΥХ����� +��[w3m-dev 00774] file �� close ϳ�줬���ꡢfile��������open�Ǥ��ʤ��ʤ�Х��� + ������ + +From: SASAKI Takeshi <sasaki@sysrap.cs.fujitsu.co.jp> +��[w3m-dev 00598] ID_EXT��Ϣ�Х����� +��[w3m-dev 00700] 'o' �ǥ��ץ����������̤˹Ԥ��ȡ�ɽ���Ѵ��������ɡפ���� + EUC-JP �ˤʤäƤ��ޤ��褦�Ǥ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +��[w3m-dev 00602] <title>...</title>�ν������˲��̤˲��Ԥ�����Х��ν����� +��[w3m-dev 00617] <table> ��� <blockquote>(�ޤ��� <ul>, <ol>, <dl>) ��ˤ��� + <table> ��ɽ�������������ʤ�Х��ν����� +��[w3m-dev 00675] (0xa0) ��ɽ���Ǥ��ʤ�ü���ؤ��б��� +��[w3m-dev 00732] <!--comment --\n> �η��Υ����Ȥ����ޤ������Ƥ��ʤ��ä� + �Х��ν����� +��[w3m-dev 00750] [w3m-dev 00772] Win95��telnet���ǡ�EUC��2�Х����ܤ˥������� + �������ʸ����������Х��ν����� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +��[w3m-dev 00679] USE_SSL_VERIFY ���Ƥ���binary�� option �� save ����� SSL ���Ȥ� + �ʤ��ʤ�Х��� fix ����ѥå��Ǥ��� +��[w3m-dev 00686] w3mhelperpanel.c �ν����� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +��[w3m-dev 00692] w3m-0.1.10-pre+666 �� EWS4800 �� /usr/abiccs/bin/cc ��make�� + ��file.c������ѥ��륨�顼�Ȥʤ�ޤ����� + +From: Hiroshi Kawashima <kei@arch.sony.co.jp> +��[w3m-dev 00742] w3m-0.1.9 �� mipsel �������ƥ������ư����뤿��� + �ѥå���������ޤ����Τǡ��ݥ��Ȥ����Ƥ��������ޤ���(�ѹ����� gc ��� + �Ǥ���) + +2000.5.17 +From: Hiroaki Shimotsu <shim@d5.bs1.fc.nec.co.jp> +��[w3m-dev 00543] personal_document_root�������ʤ��ʤäƤ���Х��ν����� +��[w3m-dev 00544] local �� <a href="foo/">foo/</a> �Τ褦�� anchor ��é��Ȥ� + foo �� index.html �Τ褦�� file ��¸�ߤ����顢 + dirlist ������ˤ������ɽ������ patch ��������ޤ����� + option �Ǥ��� file ̾����ꤷ�ޤ���(��Ĥ���) + ���� document_root ��Ÿ������褦�ˤ��ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��[w3m-dev 00545] w3m -num < file ���ͤ� -num ���ץ�����ɸ������(�ѥ���)�� + ���˻Ȥä����� 'v'(view HTML) �ǹ��ֹ�ޤǤ���������� + ���ޤ��Х��������ޤ����� +��[w3m-dev 00557] -dump ������˰���ե����뤬�ä��ʤ��Х��ν����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +��[w3m-dev 00568] <table>��ǡ�<tr>..</tr>�γ���<blockquote>������� + ɽ�����������ν����� + +2000.5.16 +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��[w3m-dev 00487] termcap��sr��̵���Ф����������ư���褦���ɡ� +��[w3m-dev 00512][w3m-dev 00514][w3m-dev 00515] ����������Ƚ��β��ɡ� +��[w3m-dev 00530] w3m�� <ISINDEX> ��Ȥä�cgi�ޤ������Ǥ��ʤ��Τ� + ���Ȥ�����patch�Ǥ��� +��[w3m-dev 00537] URL��β��Ԥ�����褦�˲��ɡ� +��[w3m-dev 00542] ��HTML�����¿��frameset�ΰ����Ρ�¾��HTML��frameset�ˤ� + ���פ��б��� + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +��[w3m-dev 00488] id°���Υ��ݡ��Ȥ��Դ����Ǥ��ä�����ν����� +��[w3m-dev 00497] configure �� IPv6�δĶ���ư���Ф���褦�˲��ɡ� + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +��[w3m-dev 00489] + �� USE_GPM��USE_SYSMOUSE���������ʤ��Ķ����ȡ� USE_MOUSE��������� + �Ƥ��Ƥ���{k,x}term��Ǥ⡢�ޥ������Ȥ��ʤ��� + �� SSL�Υ��饤�����ǧ�ڤ��᤹��ڡ��������ޤä���ɽ������ʤ��� + �� -o ���ץ����Υѥ���������ɽ�������륪�ץ������ɲá� +��[w3m-dev 00519] I ���ޥ�ɤ˴ؤ��륻�����ƥ��ۡ���ν����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��[w3m-dev 00498] �ե�����̾�䴰�ǡ� / (root)����ξ�����֤Ǥ��ʤ��ʤä� + �����Х��ν����� +��[w3m-dev 00508] ���λ��꤬����ʤ��ʤäƤ����Х��ν����� +��[w3m-dev 00518] I ���ޥ�ɤ˴ؤ��륻�����ƥ��ۡ���ν����� +��[w3m-dev 00535] �ޥ�����GPM/SYSMOUSE�б��ΥХ������� + +From: Kazuhiro Nishiyama <nishiyama@mx1.tiki.ne.jp> +��[w3m-dev 00503] cygwin��$extension�����������ꤵ��Ƥ��ʤ��ä��Τ� + �ʤ����Ƥߤޤ������Ĥ��Ǥˤ�������Who are you?�ä�ʹ�����Τ⽤���� + �Ƥߤޤ����� + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +��[w3m-dev 00516] form��ʸ�������������Ȥ��ˡ�safe��ʸ���������� + ���ʤ��褦�ˤ����� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +��[w3m-dev 00471] ���̤κǽ�˥������ڡ�����ɽ���������Ȥ��ˡ���� + ���������ƥ��֤ˤʤ�ʤ����Ȥ�����Τ�������ѥå��Ǥ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +��[w3m-dev 00539] proxy�ν������ΥХ������� + + + +2000.4.24 +From: aito +�������Ƥ��ʤ��Хåե��ϥ��꤫��������褦�ˤ��Ƥߤ��� +��file:// �����ǥ�������ե�����˥����������Ƽ��Ԥ�����硢 + http:// �����ʤ��褦�ˤ����� +��GPM�ޤ���SYSMOUSE��ȤäƤ��ʤ��ȡ�xterm/kterm��ǥޥ����� + �����ʤ��Х��ν����� + +From: rubikitch <rubikitch@ruby-lang.org> +��Buffer �� URL ��ե�����˥����֤��뵡ǽ�ɲá� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��FTP proxy�������ʤ��ʤäƤ����Х��ν����� +��C comment cleanup. +�����̥��������ѹ�����ȡ�����Υ������Ϥ��Ȥ� reshapeBuffer() + ���ƤФ�Ƥ����Х��ν����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +��gc �� /usr/local �ʲ��ˤ���� configure �� + found -> dones't seem to work �Ȥʤ��礬����ޤ��� +��w3m -v �Ȥ��� W3M HomePage �����ܤ��Ȥ���Ȥ����ޤ����� +��ISO-8859-1 �Ρءߡ٤ȡء�٤Υ����ɤ��ְ�äƤ��ޤ����� + �� LANG == EN �λ� 0x80-0x9F ��ɽ�����ʤ��ͤˤʤäƤ��ޤ��� + �� ISO-8859-* �ʳ��� CP??? �� Big5 �ǻȤäƤ���ͤ����뤫�⡣ +��file://host �η����λ� file:/ ���������Ƥ����Τ� + file://host/ ��Ʊ�ͤ� ftp://host/ �����������ͤ˽����� + (���ʬ����ȴ����Ǥ���)�� + �ʤ���file://user@host/hoge �� file:/hoge ����������ΤǤ����� + ���Τ虜�虜������������ʬ�Ǥ�פ��Ф��ʤ��ΤǤ��Τޤޤˤ��Ƥ����ޤ��� + (1999/08/31 �Ǥؤ� patch ���ä�) + + +2000.4.21 +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> + �� ƿ̾FTP���������ѥѥ���ɤ�ʸ����@�פǽ���äƤ����硢 FTP�����Ф� + ����³�˻Ȥ��륽���åȤ���FQDN����ơ��ѥ���ɤ��ɲä��롣 + �� ���ץ����������--���ץ����̾=�͡פȤ������ǡ����ޥ�ɥ饤�� + ��ǽ�ˤ��롣 + �� news URI�ǵ������������ݤˡ��Ķ��ѿ�NNTPMODE���ͤ���ʸ����ǤϤʤ��� + ���������ͤ�����Ȥ��ơ�mode�ץ��ޥ�ɤ��������롣 + �� SSL��Ϣ�ΰʲ��Υ��ץ������ɲá� + ssl_verify_server ON/OFF + SSL�Υ�����ǧ�ڤ�Ԥ�(�ǥե���Ȥ�OFF)�� + ssl_cert_file �ե�����̾ + SSL�Υ��饤�������PEM����������ե�����(�ǥե���Ȥ�<NULL>)�� + ssl_key_file �ե�����̾ + SSL�Υ��饤�������PEM������̩���ե�����(�ǥե���Ȥ�<NULL>)�� + ssl_ca_path �ǥ��쥯�ȥ�̾ + SSL��ǧ�ڶɤ�PEM���������Τ���ǥ��쥯�ȥ�ؤΥѥ�(�ǥե���Ȥ�<NULL>) + �� + ssl_ca_file �ե�����̾ + SSL��ǧ�ڶɤ�PEM���������Υե�����(�ǥե���Ȥ�<NULL>)�� + ��������SSLEAY_VERSION_NUMBER >= 0x0800�פʴĶ��Ǥʤ���̵�̤ʥ����ɤ��� + ��������ʤΤǡ� configure����disable���Ƥ������ۤ����褤�Ǥ��礦�� + + �ޤ��ºݤ�ǧ�ڤ�Ԥ���硢 ssl_ca_path�ޤ���ssl_ca_file�ǡ������Фθ��� + ��̾���Ƥ���ǧ�ڶɤξ������ (ssl_verify_server��ON/OFF�˴ط�̵��) ���� + ���ʤ���ǧ�ڤ��������ʤ��褦�Ǥ��� + +From: aito +���������л��ꤵ��Ƥ���ɽ������ҤˤʤꡢCOLSPAN��2�ʾ�� + ����COLSPAN���꤬�����ʤ��Х��ν����� +��configure�˥��ץ������ɲá� +��local file����Υ���Ȥ� Referer: ���դ��Ƥ����Х��ν����� + +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- All T/NILs are replaced with TRUE/FALSE. +- Messages are added for FTP connection. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +������������DEL���������Х��ν����� +�������Ƚ����ΥХ������� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +��FTP_proxy ������ˡ�proxy��ǧ�ڤ��᤹������н衣 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��<input_alt fid=0>��w3m�������Х��ν����� + + +2000.4.7 +From: aito +��<select>���Ф���</select>��̵���ȥ�������פ���Х��ν����� +��#ifdef USE_GPM, #ifdef USE_SYSMOUSE �� #ifdef MOUSE �� + �Ϥޤ�Ƥ��ʤ��ä��Х��ν����� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +����������ե�����ؤΥ�ɤ�ȥ�������פ���Х��� + ������ +�����Խ���DEL��Ȥ��������Х��ν����� + +From: Shin HATTORI <mituzi@he.mirai.ne.jp> +��bzip2 ���ݡ��ȤΥХ������� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��-dump, -dump_head, -dump_source ���ץ�����礷�Ƥ��� + �Х��ν����� +��-o���ץ������ɲá� +��-dump ���ץ�����Ȥ��ȥ�������פ���Х��ν����� +����å�������ɽ����˥ޥ����������ʤ��ʤ�Х��ν����� +��������ɥ��������ѹ������ޤ������ʤ��ä��Х��ν����� +����λ���γ�ǧ�Υǥե���Ȥ� n ���ѹ��� +��term.c �Ǥ� ScreenImage �γ��ݤ�ưŪ�ˤ����� + +From: Sven Mascheck <mascheck@faw.uni-ulm.de> +* There are websites using (unprintable) special characters (eg '0x96') + to 'feature' microsoft browsers. At least in the western configuration + (the only one i know), w3m doesn't check if characters are printable, + thus they confuse particularly the /xfree/ xterm (knowing more special + characters than other xterms). + Something like the attached patch could prevent this + (also only affects western version). + Instead of (superfluously) using isprint() with the locale, + it now just checks the range (pointed out by Christian Weisgerber). + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +* C++ style comments are changed into C style. + +2000.4.6 +From: lars brinkhoff <lars@nocrew.org> +ARM linux patch. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +'u'���ޥ�ɤǡ�form�μ��बɽ�������褦�˲��ɡ� + +From: patakuti +o Cygwin �Ǥ� snprintf ���ʤ��ƥ���ѥ���Ǥ��ʤ��ä��Τǽ��� +o text/html �ʳ��Υɥ�����Ȥ� -dump ���褦�Ȥ������� + ��ư�����������ä��Τǽ��� +o ��������ե�����Υե�����γ�ĥ�Ҥ� mime-type ���б��� + �ե�����˵��ҤǤ���褦�ˤ��� (¿ʬ ftp �Ǥ� Ʊ��) + + +2000.4.5 +From: ���� <hsaka@mth.biglobe.ne.jp> +��'U'���ޥ�ɤǡ����ߤΥХåե���URL���ҥ��ȥ����Ƭ�����褦�ˤ����� +��table ��� <h1>��<h1> ��������������������ʤ�ޤ��� + �ºݤˤ� frame �Ǥ��������ʤ뤳�Ȥ����ꤽ���Ǥ��� +��table ��˳��ϥ����Τʤ� </ol>,</ul>,</dl>,</blockquote> ������ȡ� + table ������ޤ���(����� HTML ������������ΤǤ���) + +From: "Shin'ya Kumabuchi" <kumabu@t3.rim.or.jp> +���̾�Υ����ɻ��� Pragma: no-cache ��Ф����Ȥ�����Х��ν����� + +From: Tomoyuki Kosimizu <greentea@fa2.so-net.ne.jp> +w3m-0.1.6��rc.c�Ǻ��٤�����Ĥ��ޤ����Τ���𤤤����ޤ��� + +2000.3.29 +From: Altair�� <NBG01720@nifty.ne.jp> + +OS/2�б������� +���������롦�ե�����Υ����ץ�˼��Ԥ�������http://���ꤷ�ƥ�� + �饤�����뤳�Ȥǡ����줬�ǥ��쥯�ȥ���ä�����dirlist.cgi���Ƥ� + �Ф����㤨�ʤ����Ȥ�����Τ��к���(url.c�θ�Ⱦ) +����X��OS/2�Ķ��Ǥ��������������褦�ˤ��� +�������ץ�����ब���ޤ��ƤӽФ���ʤ��ä��Τ��� +���ե����롦���������ǥɥ饤�֡��쥿����ͭ���ˤ��� + +From: David Leonard <leonard@csee.uq.edu.au> +after filling in a simple form + <form action="https://internal.csee.uq.edu.au/cgi-bin/login.cgi" method=POST> +a cookie is received and then w3m dumps core. + +From: Ken Yap <ken@nlc.net.au> +I have made w3m work on DJGPP (protected mode 32-bit programs running +from DOS/Win). The resulting binary after compression is only 220kB, +which means it's possible to give a floppy for a 386 with 4-8 MB memory +for browsing the web! + +From: "SHIROYAMA Takayuki" <psi@stellar.co.jp> +From: Jeroen Scheerder <J.Scheerder@cwi.nl> +MacOS X Server patch. + +2000.2.25 +From: Ambrose Li +I found a bug in <img alt=""> +handling. If alt="" is not suppressed, the line containing the img +element is not wrapped. I have verified that the bug exists in w3m +0.1.6; the bug seems to still exist in w3m 0.1.7, but I have not +finished compiling it. + +From: aito +<select> �ʤ��� <option> ���и������ core dump ����Х��ν����� +�ɥ�����Ȥ���Ƭ�� <blockquote> �����ȡ���Ƭ�Ԥ�����ǥ�� +����ʤ��Х��ν����� +application/x-bzip ���б��� +mktable, w3mbookmark, w3mhelperpanel �� GC �ν�������Ǥ��ʤ� +���Ȥ�����Х��ν����� +�ޥ����Υɥ�å�ư��岼�Ⱥ����ǰ�������ʤ��ä��Х��ν����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +�����Υ����Ǥʤ� <...>��Ȥ��ȵ�ư�������������Ȥ�����Х��ν����� +quoteShell() �Υ������ƥ��ۡ���ν����� +set_environ() �����core dump ���뤳�Ȥ�����Х��ν����� +<table width="xxx%"> ��ɽ�������ޤ������ʤ��ä��Х��ν����� +'!' �ǥ��ޥ�ɤ�¹Ԥ����Ȥ��˲��̤�����Х��ν����� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +�Ƽ����ե�����Υѥ��� // ���ޤޤ��Х��ν����� +0.1.7 �� https ���Ȥ��ʤ��ʤäƤ���Х��ν����� + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +proxy �����ꤷ�Ƥ��ơ���������³�Ǥ��ʤ��ä����ϥ��顼�ˤʤ� +�褦�˽����� +w3m ��ľ�ܰ����Ȥ���Ϳ���� URL ���� 'U' ���ޥ�ɤ����������ʤ� +�Х��ν����� + +From: sasaki@ct.sakura.ne.jp +HTML4.0 �� ID °������é���褦�ˤ��Ƥߤޤ����� + +From: Okabe Katsuya <okabe@fphy.hep.okayama-u.ac.jp> +table ����� <input type=hidden> �ʤɤ������ɽ�������Х��� +������ + +2000.2.12 +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- added GNU-style comments for all #ifdef/#else/#endif + modified: almost all files +- renamed w3mhelp_en and w3mhelp_ja to w3mhelp-w3m_en and w3mhelp-w3m_ja + (to aid in handling of additional keybindings in the future) + modified: XMakefile, XMakefile.dist, config.h, configure, help files +- corrected error in w3mhelp-lynx_en ('Japanese' link was pointing to + Japanese language help file for the w3m keybinding, not the lynx + keybinding) + modified: w3mhelp-lynx_en.html +- replaced 'Loading {URL}' message with more specific messages about + current status ('Performing hostname lookup on {hostname}' and + 'Connecting to {hostname}') + modified: main.c, url.c + +2000.2.10 +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- added support for PageUp and PageDown in list boxes (popups) + modified: menu.c + (this patch was sent to you previously) + +2000.1.28 +From: aito +mySystem() ���ѹ��������� fork/execvp ����ΤǤϤʤ��ơ�shell�� +�ü�ʸ���������פ��Ƥ��� system() ��Ȥ��褦�ˤ��Ƥߤ��� + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +w3mbookmark �β��ɡ� +1. �������Ͽ�Ǥ������Ϻ���ư�� +2. ��Ͽ�Ǥ��ʤ�������Ͽ�ѥͥ����� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> + frameɽ��buffer����buffer����ʤ�(����������ɤޤ��ϥץ�����൯ + ư)���ˡ�"Can't �ʤ�Ȥ�"�Ȥ���frame�Τ���buffer���Ǥ��롣reload�� + ��ȡ��⤦���٥���������ɤ��Ƥ��� +��ľ���ޤ�����frameset�ϡ����ΤȤ���8���ܤ��ᤦ�����ڤäƤ��ޤ��� +��������餺w3m-0.1.6����Υѥå��Ǥ������ΤȤ����������Զ�礬���뤳 +�Ȥ��狼�äƤ��ޤ��� + "q" �����ェλ���Ƥ�����Υ���å���ե����뤬�Ĥ뤳�Ȥ����� + �⤷��������target�ʳ���frame�����뤫�⤷��ʤ� + frame���meta��http-equiv="refresh"��ɽ�����ʤ��Τǡ����˺���ڡ� + �������� + +From: aito +GlobalKeymap[] ¾�Υ����ޥåפ�w3mFuncList[] ��ź����������ѹ��� +gopher:, news: �ؤΥ��������ץ����ˤ����� +searchForward, searchBackward �ΥХ������� +system()�����Ѥ���褦�� mySystem() ���ѹ��� +FORMAT_NICE ��ǥե���Ȥ� off �Ȥ���褦�ѹ��� + +From: IIMURA Takuji <uirou@din.or.jp> + ��background image �ʳ��ˡ� + APPLET ARCHIVE="" + EMBED SRC="" + ����Ĥؤ� link ����褦�ˤ��ޤ����� + �ʤΤǡ�#ifdef BACKGROUND_IMAGE_DISPLAY ���顢 + #ifdef VIEW_UNSEENOBJECTS ��̾�����Ѥ��ޤ����� + ��'o' �dz������ץ�����˥塼�� + �ָ�������Ƥ����ο�����ꤹ��פ�Ȥä� ON/OFF �����褦�ˤ��ޤ����� + cookie �ط��ǡ� + �֥��å�������Ѥ���� ON/OFF ��¾�ˡ� + �֥��å���������դ���� ON/OFF ��Ĥ��ޤ����� + ����ǡ����˼����դ��� cookie ����������ǡ� + ������ cookie �Ͽ��٤ʤ� mode �˽���ޤ��� + +From: Christian Weisgerber <w3m-dev-en@mips.rhein-neckar.de> +FreeBSD sysmouse support. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +-B -dump �ʤɤ������Х��ν����ȴ�Ϣư��������Ǥ��� +������ + ������ URL(�ե�����̾)��̵����硢������쥯��(< file)���ѥ��� + �� -B(�֥å��ޡ���) �� HTTP_HOME �� WWW_HOME �� -v ���ץ���� + �ν�˻�Ʋ���ʤ���� usage() �ǽ�λ�� + ��������С������ -dump ��ե졼��ɽ������ URL ��Ʊ���˰����ޤ��� + �� �����������ɤⶦ�̲����Ƥ��ޤ��� +������ + ������쥯��(> file )��ѥ��פξ�硢-halfdump, -dump_source, + -dump_head �Ǥʤ���С�-dump ���ꤹ���ͤˤ��ޤ����� + w3m file.html > file.txt �ǥե����ޥå��ˤʤ�ޤ��� + �� �������Ѵ��ġ���Ȥ⤹�뤿�� i18n �Ǥ⤳�����Ƥޤ��� + +2000.1.27 +From: aito +* FORMAT_NICE�����������硤<UL><LI>...</UL>��ɽ�������������ʤ� + �Х��ν����� +* pq.c, pq.h ������PQ_xxx �Τ��������� qsort ��Ȥ��褦�ˤ����� +* �ǥ��쥯�ȥ�ꥹ�Ȥ�ɽ���Ǥ��ʤ��Х��ν����� + +2000.1.25 +From: Fumitoshi UKAI <ukai@debian.or.jp> +mailto: �ǡ�mail��Ф������ C-c �� intr ������� segfault����� +�����Х��Ǥ������Υѥå��Ǥʤ���ޤ������� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> + * fm.h, frame.c, map.c, buffer.c, file.c, main.c: frameset���� + �������back���Ǥ���褦�ˡ�Buffer��������frameset���å��� + ���ƻ��Ĥ褦���ѹ���Buffer select�Τ褦��Ǥ�դλ�������ꤹ�� + �Τ����äݤ��Ǥ� + + * rc.c, main.c, frame.c: default target��_self���ѹ���ȼ�äơ� + bufferA(target���ɤ�ʤ������������Ф����̤�)���ɲä����� + �ɡ�keybind�ˤ���Ͽ���� + + * main.c (reload): frame�Ǥ�reload��ư���frameset��̤�� + frame�Τ�reload���ѹ���frame�δݤ���reload��F->R->F�ǤǤ��롢 + �Ϥ� + + * frame.c (newFrameSet): frame����table�β�����Ĥ�����ʬ�ǡ� + ���������¤㤤���Ƥ����Τ��� + + * frame.c (createFrameFile): frame���table stack��under flow + ����ޤ��ơ�table��td��th��tr�������ɬ�פ˱���ɽ�����ʤ��褦 + ���ѹ����Ĥ��Ǥ�overflow�ΤȤ��ˤϡ��ե�����κǸ��ɬ�פʿ��� + ��/table������褦�ˤʤäƤ���Ϥ� + + * html.h, url.c, file.c, menu.c, frame.c: ���֥������Ȥ���ޤ� + �� (retrieve scheme��������)��ή���char *url -> parse?URL() + -> parsedURL.scheme -> openURL() -> URLFile.scheme -> + loadGeneralFile() -> Buffer.real_scheme �Ȳ��ꤷ�ơ�openURL() + �ˤ�url��local_cgi���ä��Ȥ��ˤ�URLFile.scheme��SCM_LOCAL_CGI + ��ơ�cache file�դ����֤��褦���ѹ����Ĥޤꡢframe�ǥǥ� + �쥯�ȥ���褦�ˤʤäƤޤ���SCM_EXEC�ϡġĸ�ƨ���Ƥ����� + �� + + +2000.1.21 +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +1. conn.eventMask is set to 0 which disables reception of all types + of events. Effectively, this disables GPM support altogether. + Probably "~0" was intended, to enable reception of all types of + events. +2. conn.maxMod is set to ~0, which means that events with a modifier + key (shift, control, etc.) set are also sent to w3m. Since w3m + doesn't do anything with these events, they should rather be + passed on to other clients. Changing this to "conn.maxMod = 0" + will for example allow the use of the mouse in w3m *and* mouse + clicks with shift held down for console cut-and-paste. + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +I would like to suggest a small change to w3m's GPM support: +Rather than explicitly drawing the mouse pointer, this could be left to +the server, i.e. +- remove GPM_DRAWPOINTER() calls, +- set conn.defaultMask to GPM_MOVE|GPM_HARD. + + +From: aito +'<' �μ��˥���̾�ʳ��Τ�Τ��褿�Ȥ��ˡ�'<'�Τޤ�ɽ������褦 +�˲��ɡ� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +��������ΥХ������� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +w3m 0.1.4 ���Ф���, �ʲ��ν�����Ԥʤ��ޤ���. + ��Set-Cookie2 �� discard °������������褦�ˤ���. + �������ǤǤ�, expires °���� discard °������¾Ū�Ǥ���ȴ��㤤�� + ��̵�뤷�Ƥ��ޤ���. + ��<dl> ���������ζ��Ԥ�̵ͭ��, </dl> �����θ�ζ��Ԥ�̵ͭ���Х�� + ���Ƥ��ʤ��ä��Τ�, </dl> �����θ�ˤ�ɬ�����Ԥ�����褦�ˤ���. +����� <p>, <[duo]l> �ʤɤΥ�����³�������˶��Ԥ� 2 ������Τ��ɤ����� +��, �����Ĥ����ѹ���ԤʤäƤޤ���, �������꤬���뤫�⤷��ޤ���. +�㤨�Ф⤷ <p><p> �� 2�Զ����������ɤ����, ���Τޤޤˤ��Ƥ����Ƥ�������. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +table �� geometry ���ˤ���äȤ����Х�������ޤ���. +�ʲ�, ���ν����Ǥ�. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +table �� width=0 �ξ����б��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +inputLineHist() �ΥХ������� + +2000.1.14 +From: ChiDeok Hwang <cdhwang@sr.hei.co.kr> +When I browse http://i.am/orangeland and press 'v' to see document +info, w3m got seg. fault. +Reason was above site had the very strange frameset with only one frame. +<frameset rows="100%,*" ... > +Simple following fix was enough for me. + +From: aito +URL �� scheme ��̵����Τ��ɤߤ���Ȥ��ˡ�local �dz����ʤ��ä����� +http:// ���ꤹ��褦�ˤ��Ƥߤ��� + +configure ���ѹ���mkdir -p ��Ȥ��褦�ˤ����� + +ldHelp() ���ѹ���HELP_DIR �������ɤߤ���褦�ˤ����� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +RFC 882��header��entry��2�ԤΤȤ���multi byte�ΰ����ߥ��༣ +frame�����frameset html�λ��ȤǤ�target�����פ���褦�ѹ� +target="_parent" +frame���form�ǡ������ԻĤ�URI���Ǥ���Τ��� +�����ѿ����ؿ������Ƚ���ͤ�������ɲä���¾ + ��������_parent�μ����ϡ�navigator�Ȳ��㤦�äݤ��Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +progress bar �� graphic ʸ����Ȥ� +ɬ�����⤽��ۤɤʤ��Τǡ�ȿž�� '|' �����Ѥ��Ƥߤޤ����� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +���٤�, ���̤α�ü��ʸ�����ä��Ƥ��ޤ���������Ȥ�������Ǥ�. +�ʲ��Υѥå����н褷�ޤ���. + +2000.1.12 +From: aito +word fill��������Ƥߤ��� +(�ɥ�����Ȥʤ��� config.h ����ǥ��å��� #define FORMAT_NICE +�Ȥ���ư����) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> + >> w3m -halfdump �λ���$HOME/.w3m �۲���w3m* �ǻϤޤ�ե����뤬 + >> �Ĥ�褦�Ǥ��� +���켫�Τϰʲ��� patch �Ǥ����Ȼפ��ޤ��� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +"w3m ."�Ȥ�w3mhelperpanel ��ư��ʤ��ʤäƤޤ����� +�ʲ���configure �ؤΥѥå��Ǥ��� + +2000.1.11 +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> + readHeader()�Ǥ�header��ɽ���Ƚ������ڤ�ʬ���ȡ�base target�λ���� +���Ǥ�target��Ȥ������Τȡ�MIME encoded-word�֤ζ���ν����ߤ��� +������̤Υѥå��Ǥ��� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +freshmeat (http://freshmeat.net/appindex/1999/06/09/928951047.html) ��, +Yahoo! �� cookie �����ޤ��Ȥ��ʤ��Ȥ��ä��Τ�Ĵ�٤Ƥߤޤ���. +�ɤ����, path ����ȥ '/' �θ�˶����äĤ��Ƥ���Τ�����Τ褦 +�Ǥ�. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +table �Υ�����ȥ���˰ʲ�������ν�����Ԥʤ��ޤ���. + 1. ���Τ褦�� table �� 1 ������ܤ˶��ĤäƤ��ޤ�����. +-------------------------------------------------------------------------- +<table border width="600"> +<tr> +<td>fo oo oo oo bo +<td COLSPAN=2>fooo booo fooo booo fooo booo fooo booo fooo booo fooo booo +</table> +-------------------------------------------------------------------------- + 2. <dt> ����������Ȥ�, ��������η���ְ�äƤ���. + 3. HTML 4.0 ���ɲä��줿 table ���� (<thead>, <tbody> �ʤ�) ���� + �פ���褦�ˤ���. +����¾ w3m 0.1.1 �ν�����ϳ��Ƥ���, + <sa6zouvxzg0.fsf@okaibm.hep.okayama-u.ac.jp> (Article 84) �����, + <sa6iu1pzou7.fsf@okaibm.hep.okayama-u.ac.jp> (Article 59) +�Υѥå��� + +From: aito +'!'���ޥ�ɤˤ��shell�μ¹Ԥȡ�textarea�ؤ����ϤǤΥ��ǥ�����ư�� +system() ���᤹�� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��-- ����ʸ�� >�٤Υ����פΥ����Ȥν����ˤޤ����꤬����ޤ����� +patch ��Ĥ��ޤ��� + +2000.1.7 +From: Fumitoshi UKAI <ukai@debian.or.jp> +deb ��Ĥ��ä����� patch �Ǥ��� + * XMakefile + - dependency �ˤ� $(GCLIB) ����ʤ��� $(GCTARGET) ���Ȼפ��ΤǤ����� + - help file (architecture independent)�� /usr/share/w3m == $(HELP_DIR) + bookmark (architecture dependent)�� /usr/lib/w3m == $(LIB_DIR) + �ˤ櫓�Ƥޤ� (�ޤ����� /usr/lib/w3m �Ǥ⤤���Τ���) + * file.c + @@ -386,8 +386,8 @@ + content-encoding: �θ��space�ʤ��Ƥ�褤�褦�� + @@ -3504,6 +3504,7 @@ + http://haskell.org/hugs/ �ߤ����� html �� ̵��loop �ˤ�������Τ� fix + * main.c + @@ -66,7 +66,11 @@ + libgc5-dev package�Ѥ��ѹ� + @@ -1092,7 +1100,11 @@ + LIB_DIR �� HELP_DIR ��ʬΥ�����Τ� + * proto.h, rc.c + ����� LIB_DIR �� HELP_DIR ��ʬΥ�����Τ� + * terms.c + �������ʤ��� SIGWINCH ���б����ʤ� + +2000.1.5 +From: ��� + ������Ƥ��ޤ����ĥե졼��Υѥå������Ǥ���991203�Τޤ� +���ȡ�col��row�����Τʤ��ե졼�ॻ�åȤ������˼��Ԥ������� +�Ĥ��ơ��긵��source�Ͻ������Ƥ����ơ�������ʬ�Υѥå������ +����Τ�˺��Ƥ��ޤ����� + �����ϡ�frameset.split_direction�������Ԥ鷺������check +�˼��Ԥ��뤫��Ǥ����¤ΤȤ�����col��row��ξ��Ʊ������O.K.�� +���������ǡ�split_direction��̵�Ѥˤʤä��Τǡ��ä��Ƥ��ޤ� +�ޤ����� + +From: aito +C-c ���ɤߤ��ߤ���ߤ����Ȥ��ˡ��Хå����饦��ɤ�ư���Ƥ��볰���ӥ塼�� +����λ����Х��ν����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +�ޤ���ɸ�����Ϥ�ѥ��פ����ɤ߹�������� -m ���ץ���� +����Ƥ����ߤ����Ǥ��� +�� ����������ʬ����ʤ��ΤǤ����Ƕ�褯 SEGV ���ޤ��� +�� ....�ȡ����ʤ��ս�Ĥ����Τ��ɲ� patch �Ǥ��� +�� ���̤� foo->bar->hoge �ϴ��ʤ��Ǥ��͡� + +From: (��)�ο� <sekita-n@hera.im.uec.ac.jp> +Subject: Referer: ���������ץ������ɲä���ѥå� +�����ȥ�ΤȤ���Ǥ��� +�ɤ���������Ǥ����Τ�����������`o'�ǥ��ץ������̤������ +��Referer: ������ʤ��褦�ˤ���פ�ON�ˤ��Ƥ��������� + +From: (��)�ο� <sekita-n@hera.im.uec.ac.jp> +Subject: `q'�ˤĤ��� +��Do you want to exit w3m? (y or n)�� +�Dz������Ϥ����˲��Ԥ����w3m������äƤ��ޤ��Τǡ������������ѥ� +���Ǥ�(991206��)�� + +From: aito +ľ�ܥ��ޥ�ɥ饤��ǻ��ꤷ��URL�����������ɤ���ݤˡ����Ϥ��� +�ե�����̾�κǸ�˲���ʸ�����դ��Ƥ��ޤ��Х��ν����� + +2000.1.4 +From: Sven Oliver Moll <smol0999@rz.uni-hildesheim.de> +There was one thing that's been anoying me, so I got it fixed: the +behaviour of mouse dragging. The push of the mousebutton is +interpreted of dragging the text behind the window. My intuition in +dragging is that I drag the window over the text. So I added a config +option called 'reverse mouse'. + +From: aito +Lynx-like keymap�� `M' (�����֥饦���ƤӤ���)���ɲá� + +From: SUMIKAWA Munechika <sumikawa@ebina.hitachi.co.jp> +KAME on FreeBSD-3.3 �� + #define INET6 +�Ȥ��ơ�w3m-19991203��make�����Ȥ�����make���̤�ޤ���Ǥ����� +����ϡ�etc.c:FQDN()�ǻȤ��Ƥ���PF_UNSPEC��SOCK_STREAM��sys/socket.h +���������Ƥ��뤿��Ǥ��� + +From: kjm@rins.ryukoku.ac.jp (KOJIMA Hajime / ����ȥ) +NEWS-OS 6.x ���ݡ��ȡ� + +From: aito +��˥塼ɽ�����˥ޥ����������ƥ��֤ˤʤäƤ��ʤ��ä��Х��ν����� +gcc -Wall �Ƿٹ𤬽Фʤ��褦��Ĵ���� +configure�� IPv6 ��ưȽ�ꤹ��褦�ˤ����� +(Thanks to sumikawa@ebina.hitachi.co.jp) + +1999.12.28 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +tmpPropBuf ����¸���Ƥ����ƥ������뤬���줿���᤹��ʬ�˥Х� +(ʣ���Ԥξ��)�����ꡢ���������ǽ�������Τ����Ѥ������ä��Τǡ� +�ʲ����ͤˤ��Ƥߤޤ����� +������Ƥ����ο������ꤵ�Ƥ�����ϡ� + ��+ ���ο� + �����٤�ɽ�� +������Ƥ����ο������ꤵ��Ƥ��ʤ����ϡ� + ��+ �����٤�ɽ�� +������ξ��ϡ���+ �ܡ���ɡ٤�ɽ�� + (�������Ȱ�̣���ʤ��Τ��ѹ����Ƥߤޤ���) +�����ϡ���������Τ��륢���� PE_ACTIVE �����ꤷ�� +¾�θ���(mode)�θ�Ǹ���(mode)������褦�ˤ��Ƥ��ޤ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +���ܸ��ɽ���Ǥ���(#define JP_CHARSET)���֤ǡ� +#undef KANJI_SYMBOLS �ξ��˥ơ��֥���˥塼���� +�� graph ʸ����Ȥ����ͤˤ��� patch �Ǥ��� +���ˤ��ä��ꤷ��ɽ���ˤʤ�ޤ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +������ ALT="" �λ���ɽ������ΤϥХ����Ȥ��뤵���ͤ�����:< +�Τǥ��ץ����ˤ��Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +��frame �ǹ��������ڥ����ξ�硢reload ���ˤ� frameɽ��/��frameɽ�� + �ˤ�����餺ξ���Ȥ������졢frameɽ�����ä����ˤϡ� + ����� frameɽ���ΥХåե�����������ޤ��� +��edit ��(��frameɽ���Τ߲�ǽ)�ˤ� frameɽ���ΥХåե���������ˤ� + �����������ޤ��� + (��frameɽ���Хåե���ʤ����� frameɽ���Хåե��� + �ۤȤ��̵��̣�ʤΤǺ���Ǥ����Ȼפ��ޤ���) +��) ¿�ʤΥե졼��ˤ��б��Ǥ��Ƥ��ޤ���(rFrame �����б����Ƥ��ʤ�)�� + +From: aito +HTTP �� response code �� 200 �ξ��Ǥ⡤ WWW-Authenticate: �إå� +������ȥ桼��ǧ�ڤƤ����Х��ν����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> + >> ���ܸ��ɽ���Ǥ���(#define JP_CHARSET)���֤ǡ� + >> #undef KANJI_SYMBOLS �ξ��˥ơ��֥���˥塼���� + >> �� graph ʸ����Ȥ����ͤˤ��� patch �Ǥ��� +progress ɽ������ʬ���Զ��������ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +ISO-2022-JP �λ���Ⱦ�ѥ��ʤΰ��������Ѥ��Ѵ�����ʤ� +�Զ��ν����Ǥ��� + +From: aito +name��̤����� textarea �� default �Ȥ���̾��������� +�������Ƥ��ޤäƤ����� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Location: �ǻ��ꤵ�줿URL�����֤Ȥ��ˡ��Ǹ�β��Ԥ���Ȥ� +˺��Ƥ����Х��ν����� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +��<TD>,<TH>�����������꤬���ä����ε�ư�������ʲ��ɡ� +��w3m 991203 �ǤǤ�, �����ɤ� 0x80 �ʾ�� escape ʸ�������Τޤ�ɽ������ + �Ƥ��ޤ��褦�Ǥ�. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +�����Ȥ� <!-- .... -- > �Τ褦�� -- �� > �θ�� +���ڡ���������褦�ѹ��� + +1999.12.27 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +dirlist.cgi�β��ɡ� + +From: aito +'!' ���ޥ�ɤǺǸ�� & ���դ����Ȥ��˥Хå����饦��ɤˤʤ�ʤ� +�Х��ν����� + +1999.12.14 +From: Christian Weisgerber <naddy@unix-ag.uni-kl.de> +- I have appended a small patch to add support for the Home/End/ + PgUp/PgDn keys at the FreeBSD "syscons" console. + (It would be much preferable if w3m read the key sequences from + the termcap entry rather than having them hardcoded, but this + would require a substantial rewrite.) + +From: aito +��w3m-control: �ǡ�GOTO url ��Ϳ����ȡ�����url �˹Ԥ��褦�ˤ����� +��<meta http-equiv="Refresh" content="0; url=URL"> �����ä���硤 + �������ˤ��Υڡ������ɤߤ���褦�ˤ����� +��'M', 'ESC M' �dz����֥饦����Ω��������Ȥ��ˡ������֥饦���� + �������Ƥ��ʤ��ä����ˤϡ����ޥ�ɥ饤�饳�ޥ�ɤ����� + ����褦�ˤ��Ƥߤ��� + +1999.12.8 +From: aito +Proxy-Authorization ���б��� + +1999.12.3 +From: aito +�ǥ��쥯�ȥ�ɽ���˳������ޥ�ɤ�Ȥ����Ȥ��Ǥ���褦�ˤ����� +�ǥե���ȤϺ��ܤ���� dirlist.cgi�� + +1999.12.2 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��˥塼���Хåե�������̤ǡ��������뤬������ܤΤȤ��� +�����褦���ѹ��� + +From: aito +TERM={xterm|kterm}�ξ��ˤ� GPM ��Ȥ�ʤ��褦�ѹ��� +xterm �ǥޥ�����Ȥ���硤�������ϤΤȤ������ޥ�����ͭ�� +�ˤʤ�褦���ѹ��� + +1999.12.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +HTTP_HOME�����ꤷ��Ω��������Ȼߤޤ�Х��ν����� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +������Form���ɤ��segmentation fault �����Х��ν����� +table�ι��ܿ������䤹��ʬ�ΥХ������� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +<tr> ������ align °�����б����ޤ����� +�ޤ���<th> �����ξ��Υǥե���Ȥ� align �� center �ˤ��ޤ����� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +���Ƥ� JP_CHARSET ����������Ȥ��ˡ�latin1 ��ʸ����ɽ������ʤ��褦�� + �ʤäƤ����Τ��� +��JP_CHARSET ���������fm.h, conv.c, terms.c �ˤ��ä��Τ� fm.h �ˤޤ� + � +��README.func �˹�碌�ơ�func.c �� COOKIES �� COOKIE ���ѹ� + +From: aito +HTTP header �� : �θ�˶��ʤ��Ƥ��ɤ��褦���ѹ��� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +TAB�ǥ������ư����Ȥ���TABLE����Υ����ν��֤����� +�Х��ν����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +-v ���ץ����˸¤餺�����ޥ�ɥ饤�� URL ����ꤷ������ +�������ʤ������Τǡ������ܹ����ľ���Ƥߤޤ����� +�ʲ����ͤ� patch �Ǥɤ��Ǥ��礦����������𤷤� + w3m �ե졼��.html �ե졼��.html ... +�ˤ��б����Ƥ��ޤ��� +-v ���ץ����˴ؤ��Ƥϡ�ɽ������Хåե���̵������ -v ������ +����Ƥ���Ƚ�����̤�ɽ������ޤ��� + + +1999.11.26 +From: Fumitoshi UKAI <ukai@debian.or.jp> +mailcap���˵��Ҥ��륳�ޥ�ɤΰ����� ' ' �ǰϤ�Ȥ��ޤ��¹� +����ʤ��Х��ν����� + +1999.11.20 +From: SASAKI Takeshi <sasaki@isoternet.org> +�ָ�������Ƥ����˿����դ���ץ��ץ�����ON�ˤ��� +������˥�������פ���Х��ν����� + +1999.11.19 +From: aito +XMakefile �ε��Ҥ����� +local file ��2������ɤ����Х��ν����� +<UL>�ʤɤ��ͥ��Ȥ������ˡ�</ul>ľ������ܸ줬����ȥ���ǥ�� +�������Х��ν����� +GPM�б��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +�ץ����쥹�С�ɽ����˲��ɡ� + +1999.11.18 +From: Ben Winslow <rain@insane.loonybin.net> +�ץ����쥹�С���ɽ���β��ɡ� + +From: patakuti +<input type=button>�� name ���������Ƥ��ʤ��ä����ˡ������ +name ���Ĥ����Ƥ��ޤ��Х��ν����� + +From: ��� +�ե졼��� row �� col ��ξ�����ꤷ�������н补 + +From: aito +bookmark���ޥ�ɤ�w3m���Τ���ʬΥ��w3mbookmark�Ȥ������ޥ�ɤˤ��롥 +�����ȼ�ʤ���CGI����w3m�������Ǥ���褦�ˤ��롥 + +C-s �Dz���ɽ�����ߤޤäƤ����Х��ν����� + +ʸ�����ϻ��� C-g ����ߤǤ���褦�ˤ����� + +From: hovav@cs.stanford.edu +�����ӥ塼���Τʤ������פΥե���������������ɤ���Ȥ��ˡ� +��¸��Ȥ���¸�ߤ��ʤ��ǥ��쥯�ȥ����ꤹ��ȥ�������� +����Х��ν����� + +From: minoura@netbsd.org +ሴ �Τ褦�� character entity ��Ȥ��� segmentation +fault �������뤳�Ȥ�����Х��ν����� + +From: Christi Alice Scarborough <christi@chiark.greenend.org.uk> +��������Ƥ����˿����դ�����褦�ˤ����� + +1999.11.17 +From: aito +<OL>,<UL>���Υꥹ�Ȥǡ����줬�ǽ�Υ�٥�Ǥ������������˶��Ԥ� +������褦�ˤ����� +-bookmark���ץ����ǡ�bookmark�ե����뤬����Ǥ���褦�ˤ����� + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +�����η�Ǥ�����C-r C-r �ϳ�����̵ȿ���ʤΤ� N �������Ƥ��� +�ǡ�vi �� N ��Ʊ�ͤε�ǽ��������Ƥߤޤ����� +o srchnxt(), srchprv() ���������ؿ� srch_nxtprv() ��Ƥ֡� +o srch_nxtprv() �ǰ��� 1 ���ɤ�����ϡ������� + searchRoutine �Ǥʤ�����Ƥ֡� + + +1999.11.16 +From: Kiyohiro Kobayashi <k-kobaya@mxh.mesh.ne.jp> +wu-ftpd��2.6.0�ˤʤäƤ��顢NLST���Ф��ƥǥ��쥯�ȥ�̾���֤��ʤ��ʤä� +����ˡ�w3m�ǥ�����������ȥǥ��쥯�ȥ꤬�ߤ��ʤ��ʤäƤ��ޤäƤ��ޤ��� +����ǡ�NLST�ǤϤʤ�LIST����Ѥ���褦�ˡ���¤���Ƥߤޤ����� +�Ĥ��Ǥˡ��ե���������ա���������ɽ������褦�ˤ��Ƥߤޤ����� +991028�Ǥ��Ф���patch��ź�դ��ޤ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +checkContentType() �˥Х����������Ǥޤ����� +�� �ޤ���-m ������Ƥ��櫓�Ǥ�+_+ + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��˥塼��ư�����ĥ/�ѹ����ޤ����� +��ĥ���� +��C-f, C-v �Ǽ��ڡ����ι��ܤ�ɽ������ +��C-b, M-v �����ڡ����ι��ܤ�ɽ������ +��INS �����Ȥ��� ^[[L(������)��^[[E(PocketBSD) ���ɲ� +��DEL(C-?) �ǿƤΥ�˥塼����� �� BS(C-h) ��Ʊ��ư��Ǥ� +��#define MENU_THIN_FRAME �ǥ���ѥ��뤹��Ⱥ٤�������Ȥ� + �ǥե���Ȥ� #undef�� +�ѹ����� +��Ĺ����˥塼�ξ�硢�ޥ����Ǿ岼�� "��" ��å������ + ��/���ڡ����ι��ܤ�ɽ�������ͤ��ѹ� + (����ޤǤϼ�/���ι��ܤ��ä��Τ����ݤ��ä�) +�����ط�(sub-menu)�ξ�硢�ޥ������ȳ���å������ + �ƤΥ�˥塼������ͤ��ѹ� + (����ޤǤ����ƾõ���ä�����ޥ�����������뤳�Ȥ�����ʤ��ä�) +��<, >, +, - �ؤΥХ���ɤ�� + (??-like �Ǥ�ʤ��������ͤ˻Ȥ��Ť餤�Τ�ï��ȤäƤʤ��Ȼפ��ޤ�) + +From: ������ <okada@furuno.co.jp> +lynx ��ư������������ʤ�Ǥ�����<SELECT>����������˥塼ɽ���� +�Ȥ��ˡ��ǽ��Ǹ�θ���������֤��Ȥ��Ǥ���褦�ˤ��Ƥߤޤ����� +C-a �� C-e �˥Х���ɤ��Ƥ���ޤ��� + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +From: Fumitoshi UKAI <ukai@debian.or.jp> +FTP �� Multiline reply ���б����Ƥ��ʤ��ä��Х��ν����� + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +w3m-991028-2 ����Ѥ��Ƥ���, +* buffer �������礦�� LASTLINE ��Ʊ���ͤΤȤ� + selectBuffer() �ǺǸ�� buffer ���鹹�˲��إ���������Ǥ� + �Ƥ��ޤ�, ���θ�˰�ư���褦�Ȥ��� core dump ���ޤ�. +* buffer �� LASTLINE + 1 �ʾ夢��Ȥ��� selectBuffer() ��� + ��, LASTLINE + 1 ����(1 origin ��)�� buffer �����ޤ�. + ľ��� selectBuffer() ��ȸ��ߤ� buffer ���֤�ɽ������ + �ޤ���. �ä� buffer �������礦�� LASTLINE + 1 ��Ʊ���ͤΤ� + ��, ���ذ�ư���褦�Ȥ���� core dump ���ޤ�. +* cygwin-b20.1 �Τ�. cd / && w3m . ��� usage ��ɽ������ + �Ƥ��ޤ��ޤ�. + +1999.11.15 +From: aito +HTTP ���ɤ�Ǥ���ʸ��� <BASE> ���������ꡤ���줬���ߤ� URL �� +��äƤ�����硤Referer: ���ͤ������Х��ν����� +&#xnnn; �ǥ���ȥ����륳���ɤ����äƤ������ˤ����ȥǥ����� +�Ǥ��ʤ��ä��Х��ν����� +local-CGI��Ȥ����ˡ�CGI������ץȤ� file:///cgi-bin/ �� +file:///usr/local/lib/w3m/ �ˤ�����ʳ��� CGI �Ȥ��ư��� +�ʤ��褦�ˤ����� +system() ��Ǥ�������Ȥ�ʤ��褦���ɡ� + +1999.11.11 +From: aito +feed_table() ����Υ���������ʬ��ʬΥ��gethtmlcmd() �Υ��������� +����õ������ϥå���ɽ�˲��ɡ� + +1999.11.5 +From: aito +tableɽ���κݤˡ�����ե��٥åȤ� latin-1 ��ɽ�� character entity +�������äƤ���ȡ�ɽ�κǾ����η��������Х��ν����� + +1999.11.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) + >> w3m-991028 + patch1 �Ǥ�����no menu �ǥ���ѥ��뤷�褦�Ȥ���� + >> main.c �� + >> main.c:1645: `FormSelectOptionItem' undeclared (first use this function) + >> : + >> �Ȥʤ�ޤ��͡� + >> #ifdef MENU_SELECT��#endif �ǰϤ�Ф����ΤǤ��礦���� + >> ��file.c �� S_IFDIR ���ĤäƤ��ޤäƤ���ΤǸ�� patch ������ޤ��� + >> �� local.h �� S_ISDIR �ʤɤ���������ͤ���������ͽ�� +ξ���� patch �Ǥ��� +�� Symblic link �� readlink() �ǥ����å������ͤˤ��ޤ����� +�� Symblic link �Τʤ� OS �� w3m �ä� make �Ǥ���Τ��������� + >> ��dirlist.cgi �⡢����äȶ������ޤ����� + +From: ukai@debian.or.jp +Strcat_char()�ΥХ������� + +1999.10.28 +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> + file?var=value/#label +�λ��� label ����٥�Ȥ���ǧ������ʤ���Τν����Ǥ��� +�� file?var=value#label �����ꤢ��ޤ���Ǥ����� + +From: aito +�ǥХå������ɤ��ޤޤ�Ƥ�����Τ����� + +1999.10.27 +From: ������ <okada@furuno.co.jp> +�����ˡ�w3m �� JP_CHARSET ������������֤� ISO8859-1 ��ʸ��(¢ ��) +��ɽ������ʤ�����Ƥ����ΤǤ������������狼��ޤ����Τǥѥå���ź�դ� +�ޤ��� + +From: ��� +����WEB��Ȥä����ץꥱ�������ư���ʤ��Τǡ�Ĵ�٤Ƥߤ��Ȥ����� +cookie�˥ѥ����ʤ�����ư�Navigator�����Ⱦ����ۤʤ뤿��Ȥ狼��� +���������˥ѥå���Ĥ��ޤ��� + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +CGI �Υڡ����� reload ����Ȥ��ˡ����� POST ���ä���Τ� +GET �� reload ���褦�Ȥ���Х��ν����� + +From: aito +frame���椫����é�ä��Ȥ��ˡ�Referer: ���ͤ����ߤ�frame +�ǤϤʤ������� frameset �� URL �ˤʤäƤ�����Τ����� + +configure ���ѹ�����ǥ���ߤ��롥 + +FTP �ǡ�RETR,NLST ���������Ф��Ʊ��������� 150 ����Ԥ��Ƥ������� +����ʳ��Ǥ��ɤ��褦�ʤΤǽ����� + +<select multiple>...</select> �ξ�硤��˥塼�ˤ��ʤ��褦������ + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +getshell, getpipe, execdict �ؿ��ˤ����ơ� +�Хåե�̾�˺��¹Ԥ��Ƥ��륳�ޥ��̾(Ĵ�٤Ƥ���ñ��)��ޤ��褦�ʥѥ� +����ޤ����� + +From: Colin Phipps <cph@crp22.trin.cam.ac.uk> +When a load of cookies expire w3m SEGVs on startup. + +From: pmaydell@chiark.greenend.org.uk +I was looking through the w3m source, and noticed that it defines the +following macro: +#define IS_SPACE(x) (isspace(x) && !(x&0x80)) +Now this won't work as expected if x is an expression with side effects +(it will be evaluated twice). A quick grep of the sources reveals +several places where the macro is used like this: +file.c: if (IS_SPACE(*++p)) +which is almost certainly a bug... (although I haven't tried to actually +work out what the effects of it would be). + +1999.10.21 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +source/HTML ɽ������ buffername �ν����Ǥ��� +�ޤ���<input type=hidden> �ξ��ˤϡ�nitems ����� +���ʤ��褦�ˤ��ޤ����� + +1999.10.20 + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +<dt> �� <dd> �δ֤� <p>(..</p>) �� <h3>..<h3> �ʤɤ������ +����ʹ����� bold �ˤʤäƤ��ޤ��Х��ν����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��ư�ե졼��ɽ���λ��� 'B'(backBf) �Ǹ��� HTML ��ä��ͤˤ��뤿��� +������ HTML �ΥХåե��ؤΥݥ���Ф��Ƥ����ͤˤ��Ƥߤޤ����� +�����ơ����ε�����Ȥ��ȡ�'F'(rFrame), 'v'(vwSrc), '='(pginfo) �� +�ȥ���ư���ǽ�ˤʤ뤳�Ȥ˵��Ť��ޤ��������� patch �Ǥ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +-dump ���ץ������ѻ��ΰʲ���ư��������ޤ����� +��w3m -dump < file ���ȺǸ�� \377 �����롣 +��w3m -dump -s < file �ʤɤ��������Ѵ�����ʤ��� + -num, -S �ʤɤ�����ʤ��� +��w3m -dump -T text/plain < file ��������Ϥ���ʤ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��menu.c: graphic char �ط��ν���(Cygwin ����ߤ� terms.c ��) +��terms.c: Cygwin �ξ��ˤ� T_as = T_as = T_ac = "" + graph_ok() �� T_ac != '\0' ���ɲ� +��LINES - 1 �ȤʤäƤ��ޤäƤ����ս�� LASTLINE �ˤ��ޤ����� + (����ǡ�LINES �� terms.c �ʳ�����ä��ޤ���) +��bookmark.c: KANJI_SYMBOL -> LANG == JA + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +./configure �� + #define LANG JA + #undef KANJI_SYMBOLS +�ˤʤ�褦�������� make ���� w3m �� popup menu ��Ф����Ȥ��� +�� core dump ���ޤ�. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +��frame �����ܸ줬�ޤޤ�Ƥ��ʤ����˥������Ѵ��˼��Ԥ���Х��ν����� +��mouse_init() �����¦�ˡ� +��doc/menu.submenu �����ܸ줬���äƤ��ޤ���(_o_) + +From: SASAKI Takeshi <sasaki@isoternet.org> +1. Location: �إå��ǰ�ư����Ȥ��ˡ���Ȥ� URI �˥�٥뤬 +�Ĥ��Ƥ����餽�Υ�٥���� URI ���������դ���褦�ˤ����� +2. local CGI �� REMOTE_ADDR ���ʤ��� 128.0.0.1 �ˤʤäƤ��� (^^;) +�Τǡ�127.0.0.1 �ˤ����� + +1999.10.15 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> + 1. cookie �� name ����Ӥ� case sensitive �ǹԤʤäƤ�����ʬ���Ĥä� + �����Τ���. + 2. terminal �ξ��֤ˤ�ä�, sleep_till_anykey() �ǥ������Ƥ�ľ�� + ���ξ��֤����ʤ����Ȥ���������ν���. + �ޤ�, ���ΤȤ��Υ������ϤϼΤƤ�褦�ˤ��� (Ϣ³���� disp_message() + ��ư����������Τ�). + �ޤ�, ����� sleep ���֤����Ǥ���褦�ˤ���. + 3. HTTPRequest �ΰ�������������. + �ְ�äƤ��餴���ʤ���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +configure�� lib*.a �� lib*.so ��ξ��������� -l* �� +2���դ��Ƥ��ޤ�����ν����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +�������� HTML ������������� URL ���� HTML-quote ���Ƥ��ʤ��ä��Τ��� +��frame �γƥ������� <base href=... target=...> ���ɤ�褦�ˤ����� +��file://host/�� �� ftp://host/�� �����ؤ���Ȥ��� + port �λ��̵꤬������ ftp �Υǥե���Ȥ� port(21) ��Ȥ��ͤˤ����� +��BASE �Ȥʤ� URL �����䴰������ϡ�scheme ��Ʊ�����Τߤˤ����� + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +�Хåե��Υ�ط��������� + +From: SASAKI Takeshi <sasaki@isoternet.org> +domain, path �Ȥ��Ʊ��� name �������ۤʤ� cookie �� +ʣ������줿���ˡ������� cookie ���ĤäƤ��ޤ����Ȥ� +����褦�Ǥ��� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +(cookie������Ϣ) +name �� case insensitive ����Ӥ��ʤ���Фʤ�ʤ��褦�ʤΤ�, +���ν������ɲä��Ƥ�������. + +From: aito +��~/.w3m/cookie ���ʤ����� C-k ��¹Ԥ���ȥ�������� + �����礬���ä��� +��-dump �ǥ��å��������륵���Ȥ����Ƥ����פ����Ȥ��ˡ� + ~/.w3m/cookie ����������ʤ��ä��� +��&xxx;�� Latin-1 ��ʸ����Ф��Ƥ���Ȥ��ˡ�����ʸ���ξ��� + linebreak ����Ƥ����Х��ν����� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) + o "w3m http://foo.co.jp/foo.jpg"�¹Ը塢 + "Hit any key to quit w3m:" �ȥ�å�������Ф�����λ���� + ����碌��褦�ˤ��� + o "w3m http://foo.co.jp/foo.tar.gz"�Ǽ¹Ԥ���download�塢 + w3m��λ�� usage ���Фʤ��褦�ˤ���(�嵭��Ʊ��) + o ftp �ץ��ȥ����download�桢�в��ɽ������褦�ˤ��� + o ftp �ץ��ȥ����download�桢���Ǥ��ǽ�Ȥ��� + o download���ηв�ɽ����showProgress()�ǹԤ��褦���ѹ� + o FTP_proxy �����ꤵ��Ƥ������no_proxy��ftp�����Ф���� + ž��������˹Ԥ��Ƥ��ʤ��ä����Ȥ��� + o �����ѥå���Ŭ�Ѥ�ϳ��Ƥ�����ʬ������ + o conv.c:cConvJS()���Ф��ƹԤä��ѥå��ΰ����˸���(�¼�Ū + �ˤ�����̵����)�����ä��Τǡ�������� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +proxy server �� SSL ���Ȥ���褦�˲��ɡ� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +<form enctype="multipart/form-data"> <input type=file> +�б��Τ���� patch �Ǥ��� + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +w3m-991008 �� cygwin �ǻ��Ѥ��Ƥ��� + 1. / �Ȥ���� Directory list ��ɽ������ʤ�. + 2. ��������ǥ��쥯�ȥ�Ȥ����, �ե�����ؤ� link �� + file://///foo �Τ褦�ˤʤ�. + 3. file:///windows �� load �Ǥ��ʤ�. +�Ȥ����Τ�����ޤ����Τ�, patch ���äƤߤޤ���. + +From: Fumitoshi UKAI <ukai@debian.or.jp> + % http_proxy=http://foo/bar w3m http: +�ʤɤȤ����Ȥ��� segmentation fault ���ޤ��� + + +1999.10.8 +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +ISO-2022-jp ����ʸ��ǡ��ۤʤ륭��饯�����åȤؤλؼ��� +���ߤ��Ƥ�������н补 + +From: aito +table ��� <pre>... <p>... </pre><p> �Ȥ�����������ȡ� +<pre>�γ�¦��ʸ���ɤ����ߤ�����ʤ��ʤ�Ȥ����Х��ν����� +ʸ����� anchor �ο�������륫���� short ���� int ���ѹ��� +<b><u>hoge</u></b> moge �Τ褦�ʵ��Ҥǡ�`hoge '����ʬ�˲����� +������Ƥ��ޤ��Х��ν����� + +1999.10.7 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Cookie, SSL �Υ��ݡ��ȡ� + +From: aito +configure �ǡ�lib*.a �����Ǥʤ� lib*.so ��õ���褦�ˤ����������ʥߥå� +�饤�֥�ꤷ�����äƤ��ʤ������ƥ�ؤ���θ�� + +From: HIROSE Masaaki <hirose31@t3.rim.or.jp> +From: Anthony Baxter <anthony@ekorp.com> +Host: �إå��˥ݡ����ֹ���դ��Ƥ��ʤ��ä��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +������������ʤ��ΤǤ�������٥�˰�ư�������� URL ���Ѥ��ʤ� +�ʤäƤ��ޤ����Τǡ����ν��� patch �Ǥ��� +�� http://www.ntk.net/ ���Ƥ��Ƶ��Ť��ޤ����� +�ޤ�����٥�˰�ư�������ˤϤޤ��Х������ä� copyBuffer() ��ȤäƤ��뤿��ˡ� +sourcefile ��Ʊ���ˤʤ� Buffer ��ä������˸��� Buffer �� sourcefile +��ä���Ƥ��ޤ��ޤ���¾�ˤ⡢pagerSource �� frameset ��ޤ����褦�Ǥ��� +�����ǡ�������(int �Υݥ���)���ߤ��Ƥ����ơ����줬 0 �ˤʤä���ä��褦�� +���Ƥߤޤ����� + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +<ul> ���Υͥ��Ȥ� 20 ��ۤ��������Ƥ�����Τν����Ǥ��� +��MAX_ENV_LEVEL(=20) ��ۤ����ͥ��Ȥ�̵�뤷�ޤ��� + MAX_ENV_LEVEL ��ۤ�������ɽ�����ݾڤǤ��ޤ��� + �� <dl><li> �Ȥ� <ul><dt> �Ȥ���ʿ���ǽФƤ���;_; +��MAX_INDENT_LEVEL(=10) ��ۤ����ͥ��ȤϤ���ʾ奤��ǥ�Ȥ��ޤ��� +�Ȥ��Ƥ��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Content-Transfer-Encoding: quoted-printable �ξ��ΥХ������Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +���Dz��ԤǺǸ��ʸ���������Σ��Х����ܤ��ȥ�˥塼�����������ʤä� + �����ΤˤȤꤢ�����б���(terms.c ���б����٤���) +���ե�����̾��ޥ�������ɽ��������Υ������η����� + (��������� table �ˤʤäƤ���ȻפäƤ���˺��Ƥޤ���) + +From:aito +<frameset > ����� COLS= �� +ROWS= ��ξ�����ꤷ�Ƥ���ȡ��ե졼�ब���ޤ�������� +�Ǥ��ʤ��ʤ�褦�Ǥ��� + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Mime-Version: 1.0 +Content-Type: text/plain; charset=ISO-2022-JP +X-Mailer: mnews [version 1.21PL5] 1999-04/04(Sun) + +�京�Ǥ��� +w3m-990928 ��gziped �����(����)�����ޤ����� + o "w3m /tmp/hoge.Z"����褦�ˤ��� + o ��.gz �ʤɤΥ�����"Return"�����ξ�硢text/plain�� + �����gunzip����ɽ��������ʳ��Ǥ���� download����� + ���ˤ��� + o mouse����w3m ��"w3m /tmp/hoge.gz"����ȡ�w3m��λ�塢 + mouse �������ʤ��ʤ뤳�Ȥν��� + o ��.gz��w3m ��ɽ��������������Ӥ��Ĥ뤳�Ȥν��� + (��������Υѥå���ޡ���) + o download�桢download byte����ɽ������褦�ˤ���(512�� + ������) + o download�桢SIGINT��ͭ���ˤ���(DEL�����ʤɤ�download�� + ���ǤǤ���) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +�� B-encode �Υǥ����ɻ��ΥХ������Ǥ�(ȯ���ԤϺ京����)�� +�� Currentbuf == NULL �ξ��ˡ�disp_message() ����Ѥ�����硢 + ����äȴ����Ǥ����Τǽ������ޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> + >> ������������ʤ��ΤǤ�������٥�˰�ư�������� URL ���Ѥ��ʤ� + >> �ʤäƤ��ޤ����Τǡ����ν��� patch �Ǥ��� +���ν������ְ�äƤ��ޤ����Τǡ����ν����� +vwSrc ���ˤ�����褦�ʸ��ݤˤʤäƤ��ޤ����ΤǤ��ν����Ǥ��� +�ޤ����ե졼��ɽ���� target °���Τ��륢���� ESC RET �� download +���褦�Ȥ���Ȱ۾�ˤʤäƤ��ޤ����Τǡ����ν����Ǥ��� + +1999.9.28 + +From: SASAKI Takeshi <sasaki@isoternet.org> +wrap search ��Ԥʤ������ +patch ���äƤߤޤ��������Τ��Ȥ���ǽ�ˤʤäƤ��ޤ��� + +1. forward/backward search �Ǥ� wrap search(ʸ�Ϥν�ü/��Ƭ�ޤ� +��ã��������Ƭ/��ü���� search ��³����) +2. ���ץ����� search �Υǥե���Ȥο�����ѹ����롣 +3. ���ޥ�ɥ饤���ǥǥե���Ȥ������դˤ���(-W �Ȥ������ץ����� +������ƤƤ���ޤ�)�� +4. �����ˤ�� search �ο�������ؤ�(�̾�Ǥ� C-w, lynx ���Х���ɤǤ� +w �ˤ��Ƥ���ޤ���) + +From: SASAKI Takeshi <sasaki@isoternet.org> +���� w3m �� &#xnnnn(n ��16�ʿ���)�β����������褦�ʤΤ� +patch ���äƤߤޤ����� + +Change default character color to 'terminal' (do nothing). [aito] + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +linux ������� BG_COLOR �� define ���� w3m ��Ȥ���, ��λ������ +w3m �β��̤����ΤޤĤäƤ��ޤ��褦�Ǥ�. + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +��������𤷤����ե졼����� <pre> ��ľ��β��Ԥ����������ʤ��� +�����ޤ����� + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> + >> �Dz��Ԥ�겼�ǥޥ�����å�����ȡ������ΰ��֤� + >> ��������ΰ��֤������Τǵ����Ĥ����ΤǤ����� +�ʲ��� patch �Ǥɤ��Ǥ��礦�� + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +�ޥ����α��ܥ���ǥݥåץ��åץ�˥塼�������ޤ����� +����������ư���Ƥ��鳫���������������Ȼפ��ޤ��� +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> + >> ���ܤ���� patch ���ȡ�������Υ�������ξ�DZ�����å������ + >> ����������Ǥ��ޤ��ޤ��� + >> ��Ϥꡢ���ܥ���Ⱥ��ܥ���Ȥǽ�����ʬ�������������Ȼפ��ޤ��� +�������� patch �Ǥ��� + + + +1999.9.16 + +Fix a bug that renders <...> in form button as <...> tag. + + ������config.h �� #define DICT ��������ͭ���ˤʤ뵡ǽ�ǡ� +SIGSEGV ���Ƥ��ޤ� bug ��ߤĤ����Τ� fix ���� patch ��ޤ����� +����� update �λ��������ĺ����ȴ��Ǥ��� +(��¼���� uirou@din.or.jp) + +w3m ���� <BODY BACKGROUND="..."> +�Ȥ��ä������� background image ��Ѥ��������ʤ��褦�Ǥ����Τǡ� +�ʤ�Ȥʤ� patch ���äƤߤޤ����Τ� contribute ���ޤ��� +(��¼���� uirou@din.or.jp) + +From: Doug Kaufman <dkaufman@rahul.net> +I just downloaded and compiled the 19990902 version of w3m with cygwin +(b20 with 19990115 cygwin1.dll). The following patch takes care of the +compilation problems. + + +NEXTSTEP 3.3J�ˤơ�w3m(beta-990901)��make�����ΤǤ����� +local.c�ˤơ�������줺�˻Ȥ��Ƥ����ѿ�������ޤ����Τ� +�ѥå������餻��ĺ���ޤ��� +(����@�����ؤ���) + +From: ���� +Subject: �ꥹ�ȴĶ���� HR +�ꥹ�ȴĶ���� HR ��¸�ߤ��Ƥ����硤Netscape �Ǥϥ���ǥ�Ȱ��֤��� +���Ϥ���ޤ��������Ʊ�����Ȥ��Ǥ��ʤ����Ȥ�äƤߤޤ��������������Ϥ� +����ޤ���ư��Ƥ���褦�Ǥ��� + +From: ������ +Subject: latin1_tbl +HTML��� ア �Τ褦��ɽ��������ȡ�Segmentation fault �������Ȥ� +������Τǡ��ʲ��Τ褦�ˤ��Ƥ��Τ��Ǥ��ޤ���������Ǥ����ΤǤ��礦���� + +CGI �ξ��Υե�����̾���ϤΥХ�(_o_)�Ȥ��⤢�ä��Τǡ� +local CGI ���Ƥߤޤ����� +����ȡ� +���������ե����뤬�õ���(discardBuffer) +���إå���ʬ���ɤߤˤ����ʤ�(loadGeneralFile, openURL) +���������ȥ��顼�������(loadGeneralFile) +���� URL �� :///filename �Ȥ��������ʤ�(parsedURL2Str) +�ȡ����ʤ�Х��äƤޤ���T_T +���������ˤϡ�scheme �� SCM_LOCALCGI �����ꤹ��Τ�ߤ�ơ� +(SCM_LOCAL �Τޤޤˤ��Ƥ�����) is_cgi �ե饰��Ȥ��ͤˤ��Ƥ��ޤ��� +��ͳ�ϡ�scheme �Ȥ��� SCM_LOCALCGI �����뤳�Ȥ��θ���Ƥ��ʤ���ʬ���� +���ʤꤢ�ä�����Ǥ���(���ܤ���) + +������Ϥ� ^V + ���� �ξ��ˤ��ޤ�ư��ʤ���Τ������ޤ����� +�Ĥ��Ǥ�̵�̤��ѿ����������ޤ���(gcc -Wall ���ܤ���Τ�)�� +(���ܤ���) + +�ʲ�������������ޤ���: + + 1. �Ѹ��Ǥ� Linux ������Ǽ¹Ԥ����Ȥ�, ����ե�����ʸ����������. + 2. �����Ĥ��� kterm �Υ��顼ɽ�����Զ��. + EWS4800 ���� kterm �Ǥ�, termcap �� me �ǥ��顼���ꥻ�åȤ���ʤ� + �褦�Ǥ�. + ����� termcap �� op= �������, op ��Ȥäƥ��顼��ꥻ�åȤ���� + ���ˤ��Ƥߤޤ���. + 3. ����¾������. +(��������) + +�ɤ�����ĤΥХ��������Τ褦�Ǥ�. +��ĤϤ��ʤ��礭�ʥХ���, �ʤ�����������̵꤬���ˤʤäƤ��ޤ���. +�⤦��ĤϥХ��Ȥ������ϻ��ͤǤ���, ��ư�����������˴ݤ��Ȥ���, �� +�Ȥ�Ʊ������������ǰ�2ʸ�������Ф��������Ȥ�������Ǥ�. +(��������) + +w3m �� download ������� gunzip �ΰ����˴��Ĥ��Զ�礬����ޤ����� +���ե졼���ޤ���̤��� download ���褦�Ȥ���Ȱ۾�ˤʤ� +(���ܤ���) + +���ν�����Ԥʤ��ޤ���: + 1. <td> �����������꤬ͭ���ˤʤä����, <table> �����꤬¸�ߤ��ʤ��� + ��� table �μºݤ����� <td> �������������ͤ˰�¸����Ȥ����Զ�� + ���������Τǥޥȥ�å������νŤߤ��ѹ����ޤ���. + 2. 1 �ԥ����������®�٤����. +(��������) + + +1999.9.3 +CGI���ѻ���URL���ϤΥХ�������(���ܤ���) + + 1. ���̤�����κݤ�ʸ�����Ƚ����ˡ�θ���ν���. + 2. ���̺������ɬ������Ƚ����ˡ�θ���ν���. + 3. no_clrtoeol �κ��. +(��������) + +w3m �Ѹ��Ǥε�ư�����������Х��ν����� +��T_eA �� '\0' �ξ��Ǥ� graphic �⡼�ɤ�Ȥ��� + # ���Ĥ���ü���Ǥ� eA ��ɬ�פʤ��Τǡ� +��menu.c �� graphic �⡼�ɤ��Ȥ��ʤ����ˤ������ˤ����� +(���ܤ���) + +1999.9.2 +�Ѹ��Ǥǥ���ѥ��뤹������Զ������� (���ܤ���) + +�Ѹ��ǤΥե������ Latin-1 ʸ����ȥ�����ʸ����Ƚ�Ǥ��Ƥ��� +�Х��ν�����(��������) + +<pre>..</pre>����β��Ԥ�̵�뤵���Х��ν�����(��������) + +" �����륯�����ȤˤʤäƤ����Х��ν�����(����������) + +file://localhost/... ���Ȥ��ʤ��ʤäƤ����Х��ν�����(���ܤ���) + + + +1999.8.31 + +���ץ������̤�¿����ʪ��������ʬ��<select>�ˤ��Ƥߤ��� + +From: hsaka@mth.biglobe.ne.jp +w3m-990820 �ؤ� patch(���Σ�) �Ǥ��� + +�������(linein.c)�Ǥ�ư�����/���ɤ��ޤ����� +���ե�����̾�䥳�ޥ�ɤ����Ϥξ��ϡ���Ƭ�Υ��ڡ����������Ƥ��� + �֤��ͤˤ��ޤ�����(�Ѥʥե�����̾������Ƥ��ޤ����Ȥ����ä�����) +���䴰��ư����������ޤ����� + (1) LOAD(V), SAVE_LINK(a, ESC RET) + ��˥ե�����̾���䴰 (CPL_ALWAYS) + �䴰�����ϡ�TAB �� SPACE + flag = IN_FILENAME + (2) SAVE(ESC s), SAVE_SREEEN(S) �� "|" �ǻϤޤ�ȥѥ��פȤߤʤ���� + SHELL(!), PIPE_SHELL(#), READ_SHELL(@) + C-x ���䴰��ǽ��ȥ��롣�ǽ��ͭ�� (CPL_ON) + �䴰�����ϡ�TAB �Τ� + flag = IN_COMMAND + �� ������Τ褦�ʥ��ޥ�ɤ��䴰�Ͻ���ޤ��� + (3) GOTO(U), SEARCH(/), �ե���������Ϥʤ��̾������ + C-x ���䴰��ǽ��ȥ��롣�ǽ��̵�� (CPL_OFF) + �䴰�����ϡ�TAB �Τ� + flag = ̵�� + (4) �ѥ���ɤ����� + ����䴰���ʤ� (CPL_NEVER) + flag = IN_PASSWORD + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> + +local �ե�����̾��Ÿ������ɤ��ޤ����� +��doc-jp/README.local �� NG �ȤʤäƤ����ե�����̾�ΤۤȤ�ɤ� + Ŭ���ʤ�Τ�Ÿ�����ޤ���doc-jp/README.local �Ϻ�����Ƥ��������� +��foo/../bar �� foo/./bar �ʤɤϾ��û�̤��ޤ��� + FTP �ξ���û�̤��ޤ���HTTP ��û�̤��ޤ��� +��# �ǻϤޤ� local �ե�����̾�ξ��Ͼ�˥ե�����̾�Ȥߤʤ��ޤ��� + (����ޤǡ�directory/#1 �η��ϰ����Ƥ��ޤ���Ǥ�����) +��file://FOO/bar �η��ξ��ϡ�Cygwin �Ǥ� FOO ��ɥ饤��̾�Ȥ��Ƥߤʤ� + ����ʳ��Υ����ƥ�Ǥ� ftp://FOO/bar �� FOO ��ۥ���̾�Ȥߤʤ��� + FTP ���ޤ��� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +ɽ�����������Х��ν����� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> + +�����Ǥ�. + +Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> writes: + +> >> �Ȥ��ǡ�pipei(`@'��`#') ��Ĵ�٤Ƥ�����ˡ� +> >> `@' ��Ȥ��ȸǤޤäƤ��ޤ����ݤ˽Ф��路���ΤǤ����� +> >> �Ƹ����������餷�㤤�ޤ����� +> >> OS �� EWS4800 �Ǥ�������Ӥ�����Ƥ��ꤷ�ޤ��� +> +> FreeBSD �Ǥ�Ƹ����ޤ����������Ϥޤ�ʬ����ޤ��� + +����äȻ��֤��Ǥ����Τ�, Ĵ�٤Ƥߤޤ���. +shell ��ư��������, ����ޤǤ� shell �� output �� deleteBuffer �Ǿä��� +����꤬����ΤǤ���, ���� buffer �� Currentbuf �ΤȤ��Ǥ�ä��Ƥ� +�ޤ��ޤ�. +���θ� pushBuffer �ˤ�äƿ����� shell output �� Currentbuf ������ +�ɲä��褦�Ȥ���, ���ȸ夬����� Buffer list ���Ǥ��Ƥ��ޤ��ޤ�. +���Τ���, 3 ���ܤ˼¹Ԥ����Ȥ��� pushBuffer �����̵�¥롼�פˤʤäƤ� +�ޤ��褦�Ǥ�. + +�����, buffer == Currentbuf �ʤ� Currentbuf �� Currentbuf->nextBuffer +���ѹ����Ƥ��� buffer ��ä��褦�ˤ��Ƥߤޤ���. +�Ĥ��Ǥ˶�ͭ�Ǥ������ʥ����ɤϤޤȤ�Ƥߤޤ���. + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +���ܤǤ��� + +�طʤο���Ȥ���褦�ˤ�����(#define BG_COLOR)�λ�¿�ʽ����ȡ� +�Ĥ��Ǥ� kterm �ʤɤ�ü���� foreground, background ��Ȥ��� +�ͤˤ��Ƥߤޤ�����(�ꥯ�����Ȥ��줿�Τ�^^;) +���ȡ��٤��������Ȥ��Ǥ��� + +�����ɤˤĤ��ơ� +��clrtobotx, clrtoeolx �ΰ�̣���Ѥ��Ƥ��ޤ��� + clrtobot, clrtoeol �� term.c �ʳ��ǻȤ����ȤϤޤ��ʤ��Τ� + �ष����clrtobotx �� clrtobot ��̾�դ��������������⡣ +��term.c �� l_prop �� int �ˤ��Ƥ��ޤä��ΤǤ���ä� + ��������⤷��ޤ��� + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +�����Ǥ�. + +ü���� foreground ����Ȳ��ꤷ�Ƥ�����ʬ������Τ�, �������ޤ���. + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +��990820-hsaka7.patch + ����(`/', `?')�Ǹ���ʸ���� \0 �ξ�������θ���ʸ�������Ѥ��� + NULL(C-c) �ξ��ϸ������ʤ��褦�ˤ���(vim �� less ����Ʊ��ư��)�� + �ޤ���'/' ���������Υǥե�����ͤ϶��Ȥ����� + ����θ���ʸ����ϥҥ��ȥ��Ȥä� C-p(��) �Ǽ��Ф����Ȥ��Ǥ��� + (lynx, vim ����Ʊ��)�� + +��990820-hsaka8.patch + etc.c �� checkType() �� \b �ΰ�����̩�ˤ��� less ��Ʊ�ͤˤʤ� + �褦�˽�����"��\b��" �Ǥ� bold �� "��" �Ȥʤ롣 + +��990820-hsaka9.patch + ������Ϥ� Shift-JIS �ξ������ܸ��ɽ��������ʤ��ʤäƤ�������ν����� + ������ checkType() �� InnerCode(EUC) ������ˤ��Ƥ������ᡣ + �����ǤϾ�� InnerCode ��Ȥ��ͤˤ����ޤ� checkType() �ϻȤ�ʤ��ͤˤ��� + (\b ����Ϥ���ɬ�פ��ʤ� �� ��®��)�� + +��990820-hsaka10.patch + term.c �� l_prop �� short ���ᤷ���� + ���ٹ��Τ��ᡢ��䥳���ɤ��ɤߤˤ����ʤäƤ��뤫�⡣ + �ޤ���ü���ο�(kterm �� foreground, background �ʤ�)��Ȥä����� + need_redraw() �����ޤ�Ư���ʤ��ä����ᡢȽ����Ĥ����Ƥ��롣 + + + +1999.8.19 +-S ���ץ����(less �� -s ��Ʊ��)���ɲá�< �京���� +sakane@d4.bsd.nes.nec.co.jp THANKS! + +����Ҥˤʤä�ɽ�Υ�������ޤ��Ǥ��ʤ��Х��� +������ + +ftp �Υ��ͥ������� close ��˺��ν����� + +':' �ǥ������ɲä������ˡ�TAB�ǤΥ�����ư�� +���֤����������Х��ν�����< +�������� okabe@fphy.hep.okayama-u.ac.jp THANKS! + +1999.8.17 +�Ķ��ѿ�LESSOPEN��褦�ˤ����� < �Ƥ����� +tnh@aurora.dti.ne.jp THANKS! + +form �Ǥ��ޤ����ܸ줬����ʤ���礬����Х��ν����� +< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +<title>...</title>��ʣ���Ԥ����äƤ����Ȥ��ˡ����ΰ����� +contents �Ȥ���ɽ������Ƥ��ޤ��Х��ν�����< +�������� okabe@fphy.hep.okayama-u.ac.jp THANKS! + +ISO-8859-1 ɽ�����ˡ�������ե��å�����饯���ǽФ� +�褦�ˤ����� + +IPv6�б��ǡ�URL��port �����ꤷ�Ƥ��ä����ˤ��ޤ�ư��ʤ��ä� +�Х��ν�����< �������� ukai@debian.or.jp THANKS! + +<table width="">�Τ褦�ʻ���ξ��ˡ��������̵�뤹��褦�ˤ����� + +1999.8.15 +ISO-8859-1 �б��� + +<PRE>�θ�� ���Ԥ�Ϣ³������ˡ����줬���٤�̵�뤵��Ƥ����Х��� +������ + +���ܤ��� hsaka@mth.biglobe.ne.jp �ˤ��ʲ��ν�����THANKS!!! +w3m (990814) �ν����Ȳ��ɤǤ��� + +[ɽ��] +��<ol> ������ type(="1","i","I","a","A"), start °�����б��� + <ul> ������ type(="disc","circle","square") °�����б�(KANJI_SYMBOLS �Τ�)�� + <li> ������ type, value °�����б��� +��Overwrite �Υ��ڥ�ߥ� +��SELECT(selBuf) �� Select��˥塼�Ǥ�ɽ���� local�ե�����ʳ��� URL �� + ɽ�������ͤˤ����� +��size °���� width °����Ʊ���ˤʤäƤ����Τ����� +���Dz��Ԥ� SELECT(selBuf) �ʤɤǤΥ����ȥ��ɽ���� & �ʤɤ��Ѵ����� + ɽ�������ͤ˽���(cleanup_str �����) + +[ư��] +���� pgBack(), �� pgFore() ��ư��� less �� more ��Ʊ�����ᤷ���� + �㤨�С�3 SPC �� 3 �Կʤ�(3 J ��Ʊ��)�� + vi-like �ˤ��뤿��ˤ� VI_PREC_NUM �� #defile ���Ƥ��������� +��Save ����(-dump �ξ��ˤ�) ~/ �� ~user/ ��Ÿ�������ͤˤ����� +���ǥե���Ȥγ�ĥ��°���� .png, .PNG, .lzh, .LZH ���ɲ� +��label �������� URL �ξ������������ɤ��Ѵ����Ƥ��� label ��õ������ + �褦�ˤ��� + +[�Хåե�] +���ե졼��ɽ����������ɽ�����̤������ڡ����ΰ���������Ū�ˤ��뤿�ᡢ + Buffer ��¤�Τ� bufferprop �� BP_FRAME(�ե졼��), BP_SOURCE(������ɽ��) + BP_INTERNAL(�����ڡ���), BP_NO_URL(���URL�ʤ�) �����ꤹ��褦�ˤ����� +����ư�ե졼��ɽ���ξ��ϡ�LOAD(ldfile) �Ǥ�ե졼��ɽ����褦�˽����� +���ե졼��ɽ�����֤� RELOAD(reload) �������ˤϥե졼��ɽ����褦�˽����� +��������ɽ�����̤� EDIT(eidtBf) �����塢�̾�� HTML ���̤ˤʤäƤ��ޤ��Τ����� +��������ɽ�����̤Ǥ� RELOAD(reload) �Ǥ����ͤˤ����� +�����ץ����ɽ���������������ڡ�����ɽ�����Ƥ������ GOTO(goURL) �� + �Ȥ��ȥ��ܡ��Ȥ��뤳�Ȥ����ä��Τ��� +�����ץ����ɽ���������������ڡ�����ɽ�����Ƥ�����˳����֥饦���� + Ω���夲��Ȱʾ�۾�ʥڡ�����ɽ�����뤳�Ȥ����뽤�� +���ե졼��Τ���ڡ�����ɽ�����Ƥ������������ɥ��������Ѥ�ä����� + ~/.w3m/w3mframe* �ΰ���ե����뤬�ĤäƤ��ޤ����ȤΤ���Х������� +��editBf() �� gotoLine() �ˤΰ��֤��������������֤���¸����ʤ���Τν����� + �ޤ���ľ��� arrangeCursor() ���ɲá� +��editSrc() �� arrangeCursor() ���ɲá� + +[����¾] +��func.h��doc-jp/README.func, doc-jp/README.keymap, doc-jp/menu.*, + doc-jp/keymap.* �����ܤˤʤäƤ����� +��demo �Υ��������������Ƥ����� +���ޥ˥奢����ˡ������ڥ��(����)�ˤ����� C-z ���ɲá� + ʸ����ɤ߹��ߤ�����(���)�� C-c (���Τˤ� stty �����ꤹ�� intr �� + �����륭���������Х�����ѹ��Բ�) �Ǥ��� +��Strupper() �� Strlower() �ˤʤäƤ����� +�������ޥåץե����롢��˥塼����ե����롢mailcap �� config.h �� + �ޥ�������ˤ����� + #define KEYMAP_FILE "~/.w3m/keymap" + #define MENU_FILE "~/.w3m/menu" + #define USER_MAILCAP "~/.mailcap" + #define SYS_MAILCAP "/etc/mailcap" + +1999.8.14 +��������(okabe@fphy.hep.okayama-u.ac.jp)��������ѥå������̺��ѡ� +�ѹ���¿���ƽ���ʤ���THANKS!!! + +gzip �ǰ��̤��줿ʸ���ɤ��褦�ˤ�����IPv6���б��� +< �������� ukai@debian.or.jp THANKS! + +lynx�������Х���ɤ�help�������< ��ƣ�礵�� satodai@dog.intcul.tohoku.ac.jp +THANKS! + +frame ����� form �����ä��Ȥ��ˡ�����form������������������ɤ� +���EUC�ˤʤäƤ����Զ��ν�����< �Ƥ����� tnh@aurora.dti.ne.jp +THANKS! + +1999.7.29 +�����ȥ뤬 * �ǻϤޤ�HTMLʸ��� 'E' ���Խ��Ǥ��ʤ��Х��ν����� +< ���Ť��� shim@nw.bs1.fc.nec.co.jp THANKS! + +ɽ������2pixel�ʲ��ξ��ˡ��ؿ� log_like �� 0 ���֤������ +�����ʤ��ʤäƤ��ޤ��Х��ν�����< ���ܤ��� hsaka@mth.biglobe.ne.jp +THANKS! + + �� <ul>�Υͥ��Ȥ� 10��ۤ�����ν������Դ������ä��� + �� ��˥塼�ι��ܤ�Ĺ������������ۤ�����˥�˥塼�������Х��ν��� + �� �ե���������ϻ��ˡ�C-c�������ǥե�����ͤ��֤��褦�ˤ����� + �� func.h��doc-jp/README.func, doc-jp/README.keymap, doc-jp/menu.*, doc-jp + /keymap.*�����ܤˤʤäƤ����� + �� demo�Υ��������������Ƥ����� + �� �ޥ˥奢����ˡ������ڥ��(����)�ˤ����� C-z���ɲá� + ʸ����ɤ߹��ߤ�����(���)�� C-c (���Τˤ� stty�����ꤹ�� intr�ˤ����� + �����������Х�����ѹ��Բ�)�Ǥ��� + �� doc/FAQ.html�� href= ftp�� '"'��ȴ���Ƥ����Τ��� +< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +1999.7.16 +http://location/#label �η���URL�Ǥ� #label ���٥�Ȥ��ơ� +file:///#file �ξ��ˤ����̤� #file ��ե�����̾�Ȥߤʤ��褦�� +URL�β����ѹ�������< �����ڤ��� sasaki@isoternet.org THANKS! + +'E'�ǥ��������Խ������Ȥ���̵�¥롼�פ˴٤뤳�Ȥ�����Х��ν����� +< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +local CGI �ǡ��Ķ��ѿ� CONTENT_TYPE ���Ϥ������פ��ְ�äƤ����� +(application/x-www-form-urlencoded �ˤ��٤��Ȥ����� +x-www-form-urlencoded �ˤ��Ƥ���) + +���̤�ꥵ��������ȡ��Хåե������Կ��ȸ��߹Ԥ�����Ƥ��ޤ��Х� +�ν�����< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +'w' ��������ư����Ȥ��ˡ��Ǹ�ιԤǼ¹Ԥ����̵�¥롼�פ˴٤� +�Х��ν����� + +MANUAL_lynx.html ����< ��' ���� satodai@dog.intcul.tohoku.ac.jp +THANKS! + +1999.7.13 +linein.c�Ǥ�JIS���Ϥ����ޤ������ʤ��Х��ν�����(��öľ�����Ĥ�꤬�� +SJIS�Ǥ����Ϥ����ޤ������ʤ��Х����������Ƥ�����) + +form �� RESET�ܥ�����ȡ�HIDDEN°�����ͤޤǥ��ꥢ����Ƥ��� +�Х��ν����� +��ȤΤʤ��ե�����ξ����'='���ޥ�ɤǸ��褦�Ȥ���ȥ�������� +����Х��ν����� +US_ASCII�����Υե�����ξ����ȡ�document_code �ΤȤ����� +��Ȥ��ڤ�Ƥ��ޤ��Х��ν����� +���Ƥ��ʤ��ե������reload�Ǥ��ʤ��Х��ν����� +< ����� m-yoshi@jaist.ac.jp THANKS! + +1999.7.2 +<pre>��<nobr>������Ҥˤ����<nobr>������˲��Ԥ������Х� +�ν����� + +¸�ߤ���ե�����˾����Ȥ��˳�ǧ��å�������Ф��褦���ɡ� +local�ե������Ʊ���ե������Save��������Ƥ��ä���Х��ν����� +�ǥ��쥯�ȥ�ɽ���β��ɡ� +~/.w3m/keymap �ˤ�ꥭ���Х���ɤ�����Ǥ���褦�ˤ����� +~/.w3m/menu �ˤ���˥塼������Ǥ���褦�ˤ����� +��˥塼�Υ����Х���ɤ��ѹ��� +�Хåե������С��ե����ν����� +< �ʾ塤���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +table �ǡ�rowspan �� colspan ���Ťʤ륻�뤬������Ȥ������Х� +�ν�����< �������� okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +�ե�����Ǥʤ���ΤƤ���Ȥ��� reload ����� SEGV �� +�����뤳�Ȥ�����Х��ν����� + +1999.6.30 +<select>..</select>���˥塼�ǥ��������褦�˲��ɡ� + +1999.6.25 +'w' ��Ȥ��Ȥ��ˡ����ιԤκǸ��ʸ����2�Х���ʸ������̵�¥롼�� +�ˤʤ�Х��ν����� + +-no-mouse ���ץ����λ������ˤ�äƵ�ư���㤦����β�补 +��Ĺ��ü���κݡ����̱�ü�˥����������硢�ޥ����ˤ� +�륫�������ư���Ǥ��ʤ���礬��������β�补 +c �� u �� URL��ɽ�����Ƥ���֤ϡ�mouse��̵���ˤ��롥 + < �京���� sakane@d4.bsd.nes.nec.co.jp THANKS! + +<u><b>������ҤˤʤäƤ�����������ɽ�����Ǥ��ʤ��Х��� +������< �������� okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +<nobr>..</nobr>�����̤�����ۤ��Ƥ���Ȥ��ˡ�����ľ���Ƕ��� +���Ԥ��Ƥ�����ư���ѹ���<nobr>��ľ����Ĺ����������Ǥ����ä� +���ˤϲ��Ԥ��������Ǥʤ����ˤϲ��Ԥ��ʤ��褦�ˤ����� + +1999.6.24 +<input type=checkbox> �ǡ�VALUE °�������ꤵ��Ƥ��ʤ��ä����ˡ� +�ǥե���Ȥ��ͤ� "on" �ˤ�����Netscape ���Υ֥饦���ο����� + +1999.6.23 +�礭��0�Υե��������ǥ��������ư��� Segmentation fault +����Х��ν����� + +'\0' ��ޤ�ե�����⤽��ʤ��ɽ���Ǥ���褦�ˤ����� + +lynx�������Х���ɤǡ�'G' ��goto-line�ˡ�'g'�� goto-URL ���ѹ��� + +table ����� <H1 align=center> ����Ȥ��ȡ����θ��Ф���ۤ��� +justification ��Ŭ�Ѥ���Ƥ��ޤ��Х���fix. + +<p align=xxx> �θ��table���֤��ȡ����줬��˺��ˤʤäƤ��ޤ� +�Х��ν����������ϡ�table ������������� <pre>...</pre> +�ˤʤäƤ��ơ����� <pre> ��������Ĥ��Ƥ����������ä��� + +ñ�측����ǽ���ɲá�(ESC w, ESC W) +w �ǰ�ñ�챦��W �ǰ�ñ�캸�˰�ư���륳�ޥ�ɤ��ɲá� +< ���ܤ��� g96p0935@mse.waseda.ac.jp THANKS! + +��å������Ԥ� 300 ����ۤ���� terms.c ������ ScreenElem �� +��Ȥ������Х��ν����� +ʸ���������Ƥ���ڡ�����, ������ 0x8e ��ʸ�����ޤޤ�Ƥ���Ȥ� w3m �� +����뤳�Ȥ���������ν����� +< �������� okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +��˥塼��ǽ���ɲá� +�Dz��ԤǤΥ������Ϥ˥ҥ��ȥ굡ǽ���ɲá� +< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +1999.6.21 +minimum_length() ����� isalpha() �� IS_ALPHA()�˽����� + +lynx�������Х���ɤ��ѹ���j,k �������ư�ˡ�J,K�� +1�ԥ���������˳�ꤢ�Ƥ���< Doug Kaufman dkaufman@rahul.net THANKS! + + +1999.6.16 +XMakefile �Υ��ȡ���ǥ��쥯�ȥ�� $(DESTDIR)���ɲá� +< ���Ѥ��� yamagata@ns1.plathome.co.jp THANKS! + +-v ���ץ������դ����� + +�Dz��Ԥ˥��ɽ������Ȥ��ˡ��ޤ���� ... �Ǿ�άɽ�� +����褦�ˤ����� +dirBuffer() �� /home/../.. �ʤɤΥǥ��쥯�ȥ꤬������Ÿ�� +����Ƥ��ʤ��ä���Τν����� +�Хåե�����⡼�ɤǡ����������ͭ���ˤ��Ƥߤ��� +< ���ܤ��� hsaka@mth.biglobe.ne.jp THANKS! + +inputLine �� -no-mouse ���ץ����ˤ�餺��mouse_end(), +mouse_init() ���ƤФ�Ƥ�����Τν����� +< ���ܤ��� hsaka@mth.biglobe.ne.jp && �京���� sakane@d4.bsd.nes.nec.co.jp + THANKS! + + + +1999.6.15 +FAQ.html �����줫����< Tom Berger tom.be@gmx.net THANKS! + +1999.6.10 +<nobr>�ΰ�������ɡ� + +1999.6.9 + +HP-UX11.00 on PA-RISC2.0 �б��� +���ȡ�����ˡ����ȡ�����ǥ��쥯�ȥ꤬�ʤ��� make +������������ν�����< Dave Eaton dwe@arde.com THANKS! + +<dl compact>�ε�ư�ν�����< ���ܤ��� hsaka@mth.biglobe.ne.jp +THANKS! + +1999.6.8 + +-no-mouse ���ץ�����Ĥ����� + +<nobr>��<pre>������ҤˤʤäƤ���Ȥ��ˡ���¦�Υ����� +��Ȥ�ɽ������ʤ��Х��ν����� +<table>�����<nobr>�����ꡤ������� <br>�� <p> �ʤɤ����� +���Ȥ��ˡ�ɽ�������������Х��ν�����< �������� +okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +�ѥ�����դ��ڡ����ǡ����ä��ѥ���ɤ��ޤ����äƤ��� +�����Ϥ����Ȥ��ˡ����줬�Ȥ��ʤ��Х��ν�����< Ȫ������ +THANKS! + +char == unsigned char �ʥޥ���Ǥ��ޤ�ư���ʤ���ʬ�ν����� +< ���ʤ��蘆�� kei_sun@ba2.so-net.ne.jp THANKS! + +NetBSD/macppc �ѤΥѥå����Ѱա�< ���ʤ��蘆�� +kei_sun@ba2.so-net.ne.jp THANKS! + +table����˥����Ȥ����ꡤ���줬�Ĥ��Ƥ��ʤ��Ȥ���̵�� +�롼�פˤʤ�Х���fix. < �������� okabe@okaibm.hep.okayama-u.ac.jp +THANKS! + +1999.6.5 +�ޥ������ѻ��ˡ����ֹԤΥ��������å����ƥڡ��� +�岼�����ڡ�����ư���Ǥ���褦�ˤ����� + +1999.6.3 +�ե�����̾�κǽ��#���褿���ν����˥Х������ä��� + +���ɤߤ����ˡ����ι��ֹ�ιԤ���������褦�ˤ����� + +�ޥ����б���ʬ�β��ɡ�mouse_init()��mouse_term()�� +terms.c �˰�ư�� + +���ϡ���λ���� termcap �� ti/te ��Ф��褦�ˤ��� +�ߤ��� + +�ѥ�����դ��ڡ����ǡ����ä��ѥ���ɤ��ޤ����äƤ��� +��硤̵�¥롼�פˤʤ�Х��ν����� + +news:newsgroups �η�����URL��é�����Ȥ����Ȥ����֤���� +�Ȥ��ʤ��ݤΥ�å�������Ф����Ȥˤ����� + +1999.6.1 +�ޥ����Υɥ�å����б���< �𤵤� ytake@phys2.med.osaka-u.ac.jp +THANKS! + +1999.5.26 +�ե�����̾�κǽ�ȺǸ�� # ���褿���ˤϡ������ +��٥�Ȥ��Ʋ�ᤷ�ʤ��褦�ˤ����� + +���ֹ�ɽ����ǽ���ɲá� + +-no-proxy ���ץ������ɲá� + +�ޥ����б���< �𤵤� ytake@phys2.med.osaka-u.ac.jp +THANKS! + +&...; ���褿�������η��˥Х������ä���< �������� +okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +1ʸ�������������뵡ǽ���ɲá�< ���Ť��� +shim@nw.bs1.fc.nec.co.jp THANKS! + +<wbr> ���б��� + +table ����Ǥζ���ΰ����˥Х������ä��Τǽ����� + +table ����Ǥ� <nobr>..</nobr>�������˥ߥ������ä� +�Τǽ����� + +NNTP�ǵ�������Ф����ޥ�ɤ� article ���� ARTICLE +���ѹ���< patakuti ���� patakuti@t3.rim.or.jp THANKS! + +openURL()����ǡ�URLFile ���ѿ� uf �ν�������Դ��� +���ä�(uf.encoding ���������Ƥ��ʤ��ä�)�����ư�� +������ˤʤäƤ����������� < �ޤ��Τ��� + +1999.5.19 +table ����ˡ�; �ǽ�λ���ʤ� character entity �� +�ФƤ����Ȥ���̵�¥롼�פ˴٤�Х��ν����� +< ���ܤ��� mituharu@math.s.chiba-u.ac.jp THANKS! + +HTML �Ǥʤ���Τϥ�������ɽ�����ʤ��褦�ˤ����� + +  �Τ褦�� ; �Τʤ���Τ⡤character entity �� +���Ǥ�����ˤϤ��Τ褦��ɽ�����뤳�Ȥˤ����� + +file.c ����ǡ������Υ���ѥ���ǥ��顼�ˤʤ���ʬ�ν����� +��ư�����ץ����� +���ֹ� ���ɲá� +< ���Ť��� shim@nw.bs1.fc.nec.co.jp THANKS! + +1999.5.14 +Cygwin �����ܸ��Ȥ����ˤϡ���˺Dz��Ԥ������ +�褦�ˤ����� + +1999.5.13 +<nobr>..</nobr>�ε�ư�㤤���Ƥ����褦�ʤΤǡ������� +���Υ�������Ǥϡ��ɤ����ߤĤIJ��Ԥʤ��褦�� +������ + +1999.5.12 +J,K��1�ԥ��������뤹��ݤˡ�����������֤���¸����褦�� +������< ���Ť��� shim@nw.bs1.fc.nec.co.jp THANKS! + +1999.5.11 +text/plain �ξ��ˤ⡤�ɤߤ��߾�����Dz��Ԥ�ɽ������褦 +�ˤ�����C-c �Ǥ����Ǥˤ��н补 + +1999.5.7 +<div>,<center>�ʤɡ�justification���륿����Ȥ���� +�ˤϡ����ߤ�justification���å���������λ������ +��äƴĶ�����������褦�ˤ����� + +gc �� 4.14 �˥С�����åס� + +Content-Length �������Ф��������Ƥ������ϡ����̺Dz��� +��progress bar��Ф��褦�ˤ����� + +'=' ��ʸ������Ф��Ȥ��ˡ�HTTP�إå��ξ����ɽ������ +�褦�ˤ����� + +1999.5.6 +</OL>, </UL>��³���Ȥ��˶��Ԥ�Ϣ³���Ƥ��ޤ��Х��ν����� + +<OL>,<UL>�γ���<LI>���ФƤ����Ȥ��˲��Ԥ���褦�ˤ����� +'u'���ޥ�ɤ����Хѥ���ɽ������褦�ˤ����� +< �Ƥ����� tnh@aurora.dti.ne.jp THANKS! + +ž������� C-c �����Ǥ����Ȥ��������ޤ��ɤ�����Ƥ�ɽ������ +�褦�ˤ����� + +������ľ����¸���뵡ǽ����ɡ���������¸�Ǥ���褦�ˤ��롥 +< ����� yukihiko@yk.rim.or.jp THANKS! + + +1999.4.30 +config.h �ΰ������ڤ�Ф��� XXMakefile �����Ȥ� +���ޥ�ɤȤ��ơ�CPP�ǤϤʤ�awk��Ȥ��褦�ˤ����� + +�Хåե���HTTP�إå��ξ������¸����褦�ˤ����� + +'=' �ξ�����̤� Last Modified ���ɲá� + +���ߤΥɥ�����Ȥ� ftp �ǡ��桼��̾���ѥ���ɻ���� +�����������Ƥ�����ϡ�Referer: �˸��ߤ�URL���դ��ʤ� +�褦�ˤ����� + +���Ԥ�Ƚ�����ɡ�</OL>,</UL>,</DL>,</BLOCKQUOTE>��ľ��� +<P>��̵���ˤ����� + +���ޥ�ɤ˲��������Ǥ���褦�ˤ����� +< Ȫ������ patakuti@t3.rim.or.jp THANKS! + +�����Υɥ�����Ȥ�ե��������¸���뵡ǽ���ɲá� +< �Ƥ����� tnh@aurora.dti.ne.jp THANKS! + +1999.4.28 +ftp �Υǥ��쥯�ȥ�ꥹ�Ȥ�����ե��٥åȽ�ˤʤ�褦�ˤ����� + +ftp://username@hostname/file �����ݡ��ȡ� +FTP �Υǥ��쥯�ȥ�ꥹ�Ȥ�֥å��ޡ�������Ͽ�Ǥ���褦�ˤ� +FTP �β��̤ǥѥ���ɤ�ɽ������ʤ��褦�ˤ��� +ftp://username@hostname/file �����λ��� HTTP �Υѥ����ǧ�ڤ�Ʊ�ͤ� +add_auth_cookie ���Ѥ��ƥѥ���ɤ��ݻ�����褦�ˤ��� +< Ȫ������ patakuti@t3.rim.or.jp THANKS! + +ftp�˥�����������Ȥ���getpwuid()�ǥ桼�������Ф��ʤ��� +���顼�ˤʤ�Х������� + +<Hn>, <P>�� align °����ͭ���ˤʤ�褦�ˤ�����< ���ڸ����� +kuroki@math.tohoku.ac.jp THANKS! + +��Ƭ�˶������뤳�Ȥ�����Х��ν����� < �������� +okabe@okaibm.hep.okayama-u.ac.jp THANKS! + +���̤Υꥵ������ȥ�������פ���Х��ν����� + +ESC e �ǡ����̤�ɽ����������Խ��Ǥ���褦�ˤ����� + +1999.4.26 +ftp://user:password@host/file �������б���< �Ƥ����� +tnh@aurora.dti.ne.jp THANKS! + +"U" ���ޥ�ɤ�URL�����Ϥ���Ȥ������ߤ�URL��ǥե���Ȥˤ���褦 +�ˤ����� + +1999.4.23 +EUC��X0201���ʤ��б���< �Ƥ����� tnh@aurora.dti.ne.jp THANKS! + +1999.4.21 +term.c �ǡ�config.h �� include ���������ѹ����������ʤ��ȡ�config.h +����ǻȤ��Ƥ��� FILE* ��̤����ˤʤ롣 + +�����ɤ���URL�� "" ��Ȥ��ȥ�������פ���Х��ν�����< _tom_���� +_tom_@sf.airnet.ne.jp THANKS! + +rc.c ����αѸ�Υ�å������Υ���ܥ�̾�˴ְ㤤�����ä��� +< ����� Keiki_Sunagawa@yokogawa.co.jp THANKS! + +ftp �Υѥ���ɤ����Ǥ���褦�ˤ����� + +1999.4.18 + +<tag attr=> �Τ褦�ʥ���������ȡ������ǥ�������λ�����Ȥߤʤ���ʤ� +�Х��ν����� + +�Dz��ԤΤ����ФΥ�����ʸ������Ϥ��ʤ��褦�ˤ����������Υ��� +�ߥʥ륨�ߥ�졼���ǡ��������Τ����������뤷�Ƥ��ޤ��Τ��ɤ��� + +1999.4.9 + +Ĺ��URL�����Ϥ���ȡ�__ctype ���˲�����ơ�ɽ����ȯ������Х��ν����� + +1999.4.6 + +�ѥ���ɤ� ' ' ���Ȥ��ʤ��ä��Х��ν�����< ƿ̾��˾���� :-) THANKS! + +1999.4.5 + +�ѥ�����դ��ڡ�����������褦�˲��ɡ� + +1999.4.4 + +��������CGI���б��� + +1999.4.2 + +��������Υǥ��쥯�ȥ�ꥹ�Ȥ�������褦�˲��ɡ� + +1999.4.1 + +map ̾�˴�����Ȥ��Ȥ��ޤ� imagemap �Υ��é��ʤ��ä��Х��ν����� + +&...; �ˤĤ��ơ��Ǹ�� ; ���դ��ʤ��ä��������η������������ +������ + +-halfdump ���ץ������դ��롥����ؤ����С�(undocumented) + +"#label" �η��� URL �� parseURL2 �Dz��Ϥ������˷�̤����������ʤ� +�Х��ν����� + +1999.3.31 + +�ե�����̾�������ʥХ�URL���н褹�뤿�ᡤfollowA �� followI ����� +��URL��2�Х��ȥ����ɤ�ޤ�Ǥ����顤document_code���Ѵ����Ƥ��������� +�ˤ��Ƥߤ��������ȤäƤ⤤�������Ǥ�URL encode���Ƥ��줨�� + +configure ����� 'pxvt' �� 'rxvt' �������� + +<base href="..."> �����ä����ˡ������ʬ��URL���ȻפäƤ������� +��������base URL ���̤��ݻ����ơ����줬������ˤϤ��������URL�� +�䴰�˻Ȥ����ʤ����ϼ�ʬ��URL��Ȥ����Ȥˤ����� + +1999.3.30 + +openSocket() ����ǡ�getprotobyname() �����Ԥ����顤tcp�Υץ��ȥ��� +�ֹ�� 6 �Ƿ���Ǥ����뤳�Ȥˤ����� + +-dump_source ���ץ�����Ĥ��롥 + +���ޤ��� makeref ������ץȤ�ź�ա� + +ISO-2022-JP��ʸ���JIS X0201���ʤ����äƤ��ơ�����ľ���JIS X0208 +��������ʸ���������Ƥ����Х������� + +1999.3.29 + +sigsetjmp() �ؤ��б����Դ������ä��������� + +':' ���ޥ�ɤ� URL �Ȥߤʤ�ʸ����� ',' ���ɲá� + +configure �ǡ������ƥ�� GC library �ΥС�������Ĵ�١��Ť��ä��� +�����Ȥ����ɤ����䤤���碌�뤳�Ȥˤ����� + +sigsetjmp()/siglongjmp() ��¸�ߤ�����ˤϤ������Ȥ����Ȥˤ����� + +1999.3.24 + +���Υꥻ�å�������б����Դ������ä��Τ����� + +1999.3.23 + +<pre> ��ľ�夬���Ԥ��ä���硤�����̵�뤹��褦������ + +�ǥե���Ȥ�ʸ���ο���ü����foreground color�ʳ��ˤ��Ƥ������ˡ� +bold �θ��̤ν����ǿ����ꥻ�åȤ���Ƥ��ޤ��Х��ν����� + +HTTP�ǥե���������������ɤ�����ˡ��ե�����̾�ѹ����Ǥ��ʤ� +�Х��ν����� + +1999.3.22 + +POST��åɤα��������redirect���ޤޤ�Ƥ����Ȥ��ˡ����� +��ư��ؤΥ��������� POST�ˤʤäƤ��ޤ��Х��ν����� + +����Ҥˤʤä� table ����� <nobr>��(��ä����Τˤϡ�</nobr>��) +�Ȥ���ɽ�������Х��ν����� + +���ץ���������ɽ���������ѹ��������������ߤ����� + +��ưŪ�˾��ֹԤ˥�����URL��ɽ������⡼�ɤ��ɲá� +< ����(Seiya Yanagita)���� THANKS! + +POST ��åɤ�ʸ������Ф�����Ȥ��ˡ������� LF �Τߤξ��� +CRLF������褦���ѹ���< ���ڷ�줵�� ksuzuki@miyagi-ct.ac.jp THANKS! + +gc�饤�֥��� 4.14alpha �˥С�����åס� + +1999.3.20 + +"M" �dz����֥饦����ư����褦�ˤ����� + +1999.3.19 + +�طʤ���ˤ��Ƥ��ơ�ʸ������ˤ����Ȥ�����λ����ʸ�������� +�ʤäƤ��ޤ��Х��ν����� + +��������ե���������Хѥ��dz������Ȥ��ˡ��Хåե����դ�URL +���Ѥ��ä��Х��ν����� + +1999.3.18 + +<img alt="..."> ����� &...; �����ä���硤parsetag ����� +�ǥ����ɤ���Ƥ��ޤ��Τǡ�HTMLlineproc2 �˿��碌������ +�⤦���٥����ɤ��ʤ���Фʤ�ʤ��ä��� + +-dump ���ץ����ǡ�-F (frame��ư����)��ͭ���ˤʤ�褦�ˤ����� + +<ADDRESS>..</ADDRESS>������Dz��Ԥ���褦�ˤ����� +SPACE �����ǥХåե�����Ǥ���ȡ��Хåե��β������ΰ��֤� +�����Х��ν�����< �Ӹ����� ikehara@hepn5.c.u-tokyo.ac.jp THANKS! + +���ΥХåե����Ф��ƥ��������ư�褦�Ȥ���� core dump +����Х��ν�����< ����� yukihiko@yk.rim.or.jp THANKS! + +NO_PROXY �� IP���ɥ쥹(�ΰ���)�����Ǥ���褦�˲��ɡ� +��³��ۥ��Ȥ�ʣ����IP���ɥ쥹����äƤ�����硤�ǽ�Υ��ɥ쥹 +�ؤ���³�����Ԥ����顤���Υ��ɥ쥹����³���˹Ԥ��褦�ˤ����� +< ��ͤ��� mit@nines.nec.co.jp THANKS! + +1999.3.17 + +<table>������ҤˤʤäƤ��ơ���¦��</table>�θ夬���Ԥ� +�ʤ��ä���硤</table>��³��1ʸ������¦��table�Τ���� +�����֤���Ƥ��ޤ��Х��ν�����ʸ�Ϥǽȡ��ɤ������ɾ� +�ʤΤ��褯�狼��ʤ��ʡ� + +frame ����� <pre>..</pre>������ȡ������椬��Ԥ����ˤʤä� +���ޤ��Х��ν����� + +loadGeneralFile() ����ǡ�cbreak() �⡼�ɤΤޤ� return ���� +���ޤ����Ȥ�����Х���fix. < ��¼���� uemura@sra.co.jp THANKS! + +<caption>���Ĥ��Ƥ��ʤ��ȥ����̵�¤˳��ݤ��ޤ���Ȥ��� +�Х������fix�� + +1999.3.16 + +&hoge; �����Τ�Τ����ޤ����äƤʤ��ä��Τǡ��������������٤� +�ޤȤ�ˤʤä������б����Ƥ��ʤ� &...; ��Ȥ���ȡ��ޤ�ɽ�� +����Ƥ��ޤ����ޤ���; ��ȴ������Τ�ȤäƤ�Ʊ�����б������� + +�Ȥʤ���ɽ�ǡ�COLSPAN �� 2 �ʾ�Υ�������η���ޤ����äƤ����� +������ + +1999.3.15 + +ɸ�������Ϥ��ɤ����tty�Ǥʤ��ơ�-dump ���ץ�����Ȥ��� +���ޤ�ư���ʤ����Ȥ�����Х���fix. + +Accept-Language ���б�������Ⱦü�� + +1999.3.12 + +<table border="��">�Ȥ����ե������ڡ��������ä���atoi("��")�� +�ʤ�����ˤʤäơ�table ��ȯ��������fix. + +1�ԥ���������ǡ��Dz��Ԥ�õ�Ƥ��饹�������뤹��褦�ˤ��Ƥߤ��� +(�����äƤ鷺��路�����⡩) + +ʸ��˴ؤ�������ɽ������ "=" ���ޥ�ɤ��ɲá� + +�����ӥ塼����ư����Ȥ��ˡ�system("cmd &")��ȤäƥХå����饦��� +�˲�����ե�����ϺǸ�ˤޤȤ�ƺ�����뤳�Ȥˤ����� + +loadGeneralFile, openURL �ǡ�is_link �� is_cache ���̡��ʰ����ˤ��� +������Τ�32�ӥåȤΥե饰���ѹ��� + +���פȻפ��� allocStr() �����������Ѥ�ͽ�ۤ���롥 + +1999.3.11 + +configure �ǡ������ termcap �ߴ��饤�֥������٤�褦�ˤ����� +Ʊ���ˡ�����餷���饤�֥�꤬������ termcap ��Ϣ�롼�����ޤफ +�ɤ����ƥ��Ȥ��뤳�Ȥˤ����� + +1999.3.10 + +Str �����ѿ� s �ˤĤ��ơ�s->ptr[s->length-1] ��Ʊ���� Strlastchar(s) +���֤���������s->length==0�ξ����н补 + +<textarea>���ޤ꤫������Ƥ��ޤ��Х���fix. + +URL���Ȥ��ˡ�file ��ʬ���ʤ��ȸ��ߤ�file���դ��Ƥ��ޤ��Х���fix�� + +POST method �� form ����������Ȥ��ˡ��ǡ����κǸ��CRLF���դ��ʤ� +�褦�ˤ����� + +1999.3.9 + +resize ���б��� + +setlinescols ����� define �ǡ�TIOCGWINSZ �� TIOCWINSZ +�ȴְ㤨�Ƥ�����< ��¼���� uemura@sra.co.jp THANKS! + +Reload �ΤȤ��� Pragma: no-cache ����ꤹ��褦�ˤ����� +< �����ڤ��� sasaki@isoternet.org THANKS! + +Cygwin �Ѥˡ�����ե�����˳�ĥ�Ҥ��դ���褦�ˤ����� +�ޤ��������ʤɤΰ���ե��������Ȥ��ˡ�fopen(file,"wb") +��Ȥ��褦�ˤ����� +< ���Ĥ��� ueda@flab.fujitsu.co.jp THANKS! + +isalpha(), isalnum() ��2�Х���ʸ���ΰ����碌���Ȥ��ε�ư +��������������ˡ�ɽ�η�������뤳�Ȥ�����Х��ν����� + +<style><!-- ... --></style> �Τ褦��<style>�������椬������ +�ˤʤäƤ�����ˡ�ʸ�����Τ������ȤȤߤʤ���Ƥ��ޤ����Ȥ� +����Х��ν����� + +configure �ν������Դ������ä��� + +1999.3.8 +termcap�饤�֥��ˤĤ��ơ�ncurses > curses > termcap �ν��õ�� +�褦�ˤ����������ȼ�äơ�ncurses ������줿�Ȥ��ˡ�terms.c +�� curses ����̾���� ncurses �Ⱦ��ͤ���Τǡ�̾�����ѹ������� + +libftp ��Ȥ��Τ���ơ�ftp���������ؿ��������Ѱդ����� + +<OL> ����� <LI> ��ɽ����������Υ����� char ���� int �� +�ѹ���������3���ۤ��Ƥ�ɽ����������ʤ��褦�ˤ����� +< ��¼���� takkun@mma.club.uec.ac.jp THANKS! + +colspan �ޤ��� rowspan ���礭�������Ȥ��ˡ�abort ���뤳�Ȥ����� +�Х��ν�����< �京���� sakane@d4.bsd.nes.nec.co.jp THANKS! + +ɽ�κǽ�� <a href=".."><h1>...</h1></a>�Τ褦�����Ǥ�����ȡ� +<h1>�ˤ����Ԥ� </a> ���Ĥ��ʤ��ä��Х��ν����� + +1999.3.5 +��ư�������ˡ��Хåե��������ɤߤ��ޤ�ʤ��ä���硤�Ķ��ѿ� +HTTP_HOME ����ڡ������ɤ�褦�ˤ���������ä������� + +ISO-2022-jp ��ɽ�����Ƥ�����ˡ���λ���� US_ASCII ��ؼ����� +�褦�ˤ����� + +��������Ǥ���褦�ˤ����� + +<li> �θ�� <P> ��̵���ˡ��빽���ɤ��� + +colspan ��2�ʾ��ɽ�ˤĤ��ơ�����β����η����Ѥ��ä��Τǽ����� +�������ޤ��ѤʤȤ��������롥���������� + +/etc/mailcap ���ɤ�褦�ˤ����� + +terms.c ����� tgetstr()�����ޤ�ư���ʤ����Ȥ�����Х��ν����� +2�� fclose �� fix. < ���������� ukai@debian.or.jp THANKS! + +<form> ������°�����ʤ����ˡ�form �����ޤ���ᤵ��ʤ��Х��ν����� + +<style>..</style>��̵�뤹��褦�ˤ����� + +1�ԥ��������뤬��餫��ư���褦�˲��ɡ� + +1999.3.4 +¸�ߤ��ʤ��ե��������ꤷ�ư۾ェλ�����Ȥ��� reset_tty ���¹Ԥ��� +�ʤ����Ȥ�����Х���fix. < ���Ĥ��� ueda@iias.flab.fujitsu.co.jp THANKS! + +http://123.45.67.8/ �η����Ǥ��ޤ���³�Ǥ��ʤ����Ȥ�����Х���fix. +< ryo ���� ryo@misaki.oneechan.to THANKS! + +ɽ�����Ǥκǽ��ʸ��������ե��٥åȤǤ��ä����ˡ�����ʸ�������˶��� +1�����ä�ɽ������뤳�Ȥ�����Х���fix��HTMLlineproc1()�ǡ�RB_SPECIAL +�ե饰��Ω�äƤ������ʸ����ͤ�Ȥ���obuf->prevchar �����ꤹ��� +��˺��Ƥ�������Ǥ��ä��� + +�����ɽ���ǡ�����ե������õ�륿���ߥ�Ĵ���� + +<span> �Dz��Ԥ���Τϴְ㤤���ä��餷���Τǡ����������Τ����� +<form>..</form>�Dz��Ԥ���褦�ˤ�����< �Ӹ����� ikehara@hepn5.c.u-tokyo.ac.jp +THANKS! + +w3m -T text/html -dump < file.html > file.txt +�ǥե����ޥå��Ȥ���ư���褦�ˤ����� + +1999.3.3 +����Ҥˤʤä�table�ν����ǡ���¦��table�����dz�¦��table�� +��������ꤷ�Ƥ�����ʬ�����������ɤ��褦�����������ˤ�����ɤ� +�Τ��ɤ����褯�狼��ʤ��� + +colspan ��2�ʾ�� <td>�� width ����ꤹ���ɽ�η��������Х���fix. + +ftp �ǤΥǥ��쥯�ȥ�ꥹ�ȤΥ��������ޤ��դ��Ƥ��ʤ��ä��Х� +��fix. + +Boehm GC library �� 4.13 �˥С�����åס� + +1999.3.2 + +Cygwin32�б���< �ޤ��Ҥ����� masahiro@znet.or.jp THANKS! + +ISO-2022-JP��ʸ��ǡ������� US_ASCII �� JIS X0201 ��ؼ����ʤ��� +����äƤ�����Ǥ��������������ʤ��褦���ס� + +Editor, Mailer �ץ������ѹ���ǽ�ˤ����� + +q �ǽ�λ����Ȥ��˳�ǧ���뤫�ɤ����ץ������ѹ���ǽ�ˤ����� + +1999.3.1 +ʸ����˥����Ȥ�����ȡ�����ʹߤΥ�������ιԤ����Ԥ��줺�� +ɽ�������Х������� + +I �ǥ������ɽ��������ˡ��ӥ塼����background ��ư���褦�ˤ����� + +q �ǽ�λ����Ȥ��ˡ�����äƤ������ɤ����Ҥͤ뤳�Ȥˤ��Ƥߤ��� +��ɾ�ʤ鸵���᤹ͽ�ꡥ + +proxy ��ͳ�� ftp ���Ѥǡ��ե�����̾�����ˤ����Х������ٽ����� + +<BASE HREF=".."> ���б��� + +-dump, -cols ���ץ������ɲá� + +<U>..</U>, <DEL>..</DEL>, <INS>..</INS>, <STRIKE>..</STRIKE>, <S>..</S> +�ν������ɲá� + +��������ե����뤫��Υ��é��Ȥ��ˡ�Referer: ���դ��ʤ��褦���ѹ��� + +1999.2.26 +<div align=center/right> �����caption���դ���ɽ��Ȥ��ȡ����/���� +���Ǥ��ʤ��Х��ν����� + +<pre>..</pre>����ζ��Ԥ�̵�뤹��Х������ä��������� + +"z" �ε�ư�˥Х������ä��������� + +table ����ǡ�<input> ��Ĺ���η���ְ�äƤ����������� + +config.param �������ͤ�Ȥ��ˡ�LDFLAGS ��ʣ������ȥ��顼�� +�ФƤ����Х���fix. < ��ͤ��� mit@nines.nec.co.jp THANKS! + +ɽ�����������ɤ�JIS�ˤ�������ư��ɤ��ʤ��ä��Τ������Ʊ���ˡ� +����ѥͥ��JIS�μ����ܺ٤����٤�褦�ˤ����� +< ��ͤ��� mit@nines.nec.co.jp ���Ĥ��� ueda@iias.flab.fujitsu.co.jp +THANKS! + +SIGIOT �Υϥ�ɥ����ǡ�SIGIOT�Υϥ�ɥ���ᤷ�Ƥ��� abort() +����褦�ˤ����������ʥ�ϥ�ɥ��OS��¸���β�ä���Ū�� +< ����� takayuki@ebina.hitachi.co.jp THANKS! + +ľ��URL�����Ϥ��Ƥ⡤ľ���Υڡ����� Referer: �Ȥ��ƥ����Ф������� +���ޤäƤ����Х���fix�� + +1999.2.25 + +Makefile ����� CC=cc �ȤʤäƤ�����ʬ�� config.h ����������� +ư���褦�ˤ����� + +GET �� CGI ������Ȥ��ˡ�ʸ����� : ���ޤޤ�Ƥ���Ȥ��ޤ��Ԥ��ʤ� +�Х���fix. < ���ܤ��� manome@itlb.te.noda.sut.ac.jp THANKS! + +1999.2.24 + +"S" �ǥХåե�����¸����Ȥ��δ��������ɤ�����������(EUC)�ˤʤäƤ��� +�Τ����� + +"J","K" ��1�ԥ��������뤹�륳�ޥ�ɤ�¸�Ū���ɲá� +< ����� furukawa@ces.kyutech.ac.jp THANKS! + +Str.c �� Sprintf() �ǡ�va_arg(*,char) �Ȥ������꤬�ޥ����ä��Τǽ����� +< ��ޤƤ��� yamate@ebina.hitachi.co.jp THANKS! + +ESC : �� Message-ID ���ˤ�����ʬ�ΥХ�fix�� + +news: �Υ�����ޤ�é��ʤ��ä��Х���fix��< �京���� +sakane@d4.bsd.nes.nec.co.jp THANKS! + +1999.2.23 + +Lynx �������ޥåץե�����������configure�����٤�褦�ˤ����� +< ���ܤ��� hasimoto@shimada.nuee.nagoya-u.ac.jp THANKS! + +1999.2.22 + +ɽ����Ǥ� alt �����Ǥ��ޤ꤫������ʤ��ä��Х�(���Τˤϡ�alt ��ʸ����Ĺ�� +ɽ�κǾ����Ȥ���Ƥ��ޤ��Х�)��fix�� + +2/22 �ǥ����� + +TAB/ESC TAB �ǡ�form �ˤ����֤褦�ˤ�����< ����� furukawa@ces.kyutech.ac.jp +THANKS! + +��������ե����뤸��ʤ���Τ� "E" ���Խ����褦�Ȥ���ȡ����Υե������ +�Խ�����������������ɤ˼��Ԥ���Ȥ����Х���fix. + +"g"/"G" ����Ƭ��/�����ԤؤΥ����פ��ѹ�������ޤǤ� "g" �� +"ESC g" �˥ޥåס� < ����� furukawa@ces.kyutech.ac.jp THANKS! + +URL �Υѥ��� . ��ޤ��������������褦�ˤ�����< ���椵�� +tanaka@sp.mmlab.toshiba.co.jp THANKS! + +�Ķ��ѿ� HTTP_proxy ����ץ�������URL���äƤ����Ȥ��ˡ������ +parse ����Τ�˺��Ƥ�����< ���椵�� tanaka@sp.mmlab.toshiba.co.jp THANKS! + +�������뤬���礦������ιԤˤ���Ȥ��� "z" ��Ȥ��ȥ�������פ���Х���fix. +< ���ܤ��� hasimoto@shimada.nuee.nagoya-u.ac.jp THANKS! + +1999.2.19 + +~/.w3m/config ���ʤ��Ȥ��ˡ�~/.mailcap ���ɤޤʤ��Х���fix�� +~/.mailcap ���ʤ��Ȥ��ˡ�searchExtViewer() ����ǥ�������פ���Х� +��fix. +nextA() ����ɡ��夫����Ͽ���������ˤ����֡� +prevA() ���ɲá� + +1999.2.18 + +<dl compact> ���б��� + +����Ҥˤʤä�table����ǥ���ǥ�Ȥ��դ��Ķ���ȤäƤ��ơ������� +��¦��table���Ȥ��ʤ�����ɽ��ɽ���������Х���fix. + +1999.2.17 + +HTTP_proxy �����Ѥ��Ƥ���Ȥ�����٥���դ��� URL �����ޤ��������ʤ� +�Х���fix. + +���ץ��������ꤷ���Ȥ��ˡ��Ƽ�proxy �ξ�������ȿ�Ǥ���ʤ��ä��� + +1999.2.16 + +<script>..</script>�����Ƥ�̵�뤹�륳���ɤǡ�<script><!-- ... --></script> +�ȤʤäƤ���ȡ�</script>�ǥ�����ץȤ��Ĥ�����Ǥ⥳���Ȥ�³���Ƥ��� +�Ȥߤʤ���Ƥ����� + +�վ�Ķ��ǡ��줿ʸ������ʸ��������Ȥ�����Ƥ����� + +1999.2.12 + +caption �б��ˤȤ�ʤäơ�<tr><td>��Ĥ��ʤ���<table>����� +ʪ�����֤���ȥ�������פ���Х����������Ƥ�����fix�� + +Ĺǯ�η��ƤǤ��ä������̹����롼����ΥХ�(���̤ΰ�������������ʤ�) +�Ρ����ʤ��Ȥ������fix������ + +table.c, etc.c ����� isspace() �� IS_SPACE() ���ѹ��� +isspace()���Ѥʵ�ư(Solaris��������)�Τ����ǰ����δ��������ڡ��� +���Ȼפ��Ƥ����� + +2/10�ǥ����� + +1999.2.11 + +���ץ��������ѥͥ���ߡ�"o"���ޥ�ɡ� + +����ե������ ~/.w3m �˺�뤳�Ȥˤ��롥�ޤ�������� +~/.w3m/config �����ɤळ�Ȥˤ��롥 + +�Хåե�������������ƽ�λ����ȥ�������פ���Х� +�ν�����< �����ڤ��� sasaki@isoternet.org THANKS! + +1999.2.9 + +����°���� ' ' �ǰϤޤ�Ƥ����Ȥ��ˤ�ư���褦������ +HTMLlineproc1, HTMLlineproc2 ������ȼ��˥�������� +���Ƥ�����Τ�read_token()��Ȥ��褦���줹�롥 +<caption>..</caption>���б����롥 + +1999.2.8 + +<SPAN>, </SPAN> �Dz��Ԥ���褦�ˤ��롥Ringring ��ɽ���� +���������ä����ᡥ +���顼ɽ���Ѥ˥���ѥ��뤷�Ƥ⡤�����ɽ���Ǥ��륹���å� +��Ĥ���(-M)�� +��ư�����ץ�����ɽ����狼��䤹���ѹ��� + +1999.2.6 + +�������ʸ����ΰ�����̩����= ��³������������������� +������γ��ϤȤߤʤ��� +<script>..</script>�����̵�뤹��褦�ˤ��롥 + +1999.2.5 + +2/5�ǥ����� + +<BASE TARGET="hoge">���᤹��褦�ˤ��롥 + +1999.2.3 + +Boehm GC �� 4.13alpha3 ��up�� +location: �إå���redirect�����硤redirect��λ���� +; ���Ǥ��ڤäƤ����Τ�ߤ�� +��ä�table�б��� +frame�б�����������ư���� +table����ǰ۾�ʲ��Ԥ�ȯ������Х���fix�������ϰ���Ǥ� +�����ʤ��ۤ����ݤ��ä��� + +1999.2.2 + +frame�б�����ꡥ +<input type=image>�ΰ������ɤ��ʤ��ä��Τǽ����� +loadGeneralFile() ����ǡ��ե�����ݥ���2�� fclose() +���Ƥ����Х��ν�����< ���ܤ��� shom@i.h.kyoto-u.ac.jp THANKS! + +1999.2.1 + +<td colspan=2 rowspan=2>�ι��ܤ���ü�ʳ��ˤ��ä�����ɽ�� +���������ޤ���ʤ��Х��ν����� + +1999.1.29 + +�Ĥ�Ĺ��table���б����ơ�table ���礭�����Ϲ������� +��ưŪ�˿��Ӥ�褦�˲��ɡ� + +1999.1.28 + +file.c, conv.c ����ǡ�����Ĺ�Хåե���ȤäƤ�����ʬ�� +Str �饤�֥���Ȥ��褦���ѹ��� +ư���ǧ�� + Solaris 2.5.1 + SunOS 4.1.3 w/JLE + HP-UX 9.x + OSF/1 + +1999.1.27 + +<ISINDEX> ���б����롥 +<input type=text accept> ���б����롥 +<select multiple> ���б����롥 +<input type=radio>�ǡ��ǽ�ˤɤ������å�����Ƥ��ʤ� +���ˡ��ǽ�����Ǥ�����å�����褦�˲��ɤ��褦�Ȥ��� +���ޡ� +<td>�� rowspan �� colspan ��ξ����2�ʾ���ä����� +���ޤ���������ʤ��Х���fix. +[TAB] �ǥ��������ư����Ȥ��ˡ�����ʸ���������� +���֤褦��ư����ѹ��� +Boehm GC 4.12 ��ư�����Ȥ��ǧ�� + +1999.1.26 + +table �ǡ�rowspan ��3�ʾ��border=1�ξ��ˡ�ɽ����� +���Ԥ��Ǥ���Х���fix. + +1999.1.22 beta-990122 + +��Ͽ���ϡ�������¥С������˳ʾ夲�� +����ޤǤ� config.h �Խ���������ơ�configure ��� +ź�դ�Boehm GC library �� 4.10 ���� 4.11 �˾夲�롥 diff --git a/doc-jp/HISTORY.kokb b/doc-jp/HISTORY.kokb new file mode 100644 index 0000000..e286315 --- /dev/null +++ b/doc-jp/HISTORY.kokb @@ -0,0 +1,1220 @@ +2001/1/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01667] Re: mailer %s + Editor �� "vi %s" �ʤɤξ��� "vi file +10" �ʤɤ�Ÿ������Ƥ��ޤ��� + ���꤬���ä��Τǡ�Editor ��Ÿ���� + ��%s �������� + * %d ������С� + Sprintf(Editor, linenum, file) # ���֤ϸ��� + * �����Ǥʤ���� + Sprintf(Editor, file) + ��%s ���ʤ���� + * %d ������С� + Sprintf(Editor, linenum) file + * "vi" �Ȥ���ʸ������С� + Sprintf("%s +%d", Editor, linenum) file + * �����Ǥʤ���� + Editor file + �Ȥ��Ƥߤޤ����� + + +2001/1/24 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01661] Re: <head> + security fix. + + +2001/1/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��°���ͤ���� ", <, > & �����������Ȥ���Ƥ��뤫�ɤ��������å����� + �褦�ˤ���. + ��°������Ƥʤ�������, °���Υ����å���ȴ���Ƥ�������ν���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01663] replace addUniqHist with addHist in loadHistory() + + +2001/1/22 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01617] Re: first body with -m (Re: w3m-m17n-0.7) + ü����ꥵ������������ư���Ʊ���ˤ��ޤ���(ñ��˺��Ƥ�������)�� + �Ĥ��Ǥˡ�¿�ʤΥե졼��ǹ�������Ƥ���ڡ����λ���INFO('=') �Ǥ� + �ե졼������ɽ�������������ä��ΤǤ��ν����Ǥ��� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01621] NEXT_LINK and GOTO_LINE problem + NEXT_LINK �� GOTO_LINE �Ǥ��������Υڡ����κǽ�ιԤ˰�ư�����Ȥ������� + 1 �ڡ���ʬ���������뤷�Ƥ��ޤ��ޤ��� + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01623] Re: (frame) http://www.securityfocus.com/ +Subject: [w3m-dev 01632] Re: (frame) http://www.securityfocus.com/ + frame fix. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01624] Re: first body with -m +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01625] Re: first body with -m + pgFore, pgBack �ǡ�currentLine �����̳��Ȥʤꡢ���ġ������ʬ + ����������Ǥ��ʤ��ä��Ȥ��ˡ����ޤ�ɽ������Ƥ�����ʬ�ȿ�����ɽ������ + ����ʬ�δ֤� currentLine ����äƤ���褦�ʥѥå���Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01635] Directory list + local.c �� directory list �����������ʬ�˥Х�������ޤ����� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01643] buffername +Subject: [w3m-dev 01650] Re: buffername + buffername (title) �˴ؤ������(?)�Ƚ����Ǥ��� + ��displayLink �� ON �ξ���Ĺ�� URL ��ɽ��������� buffername ������ + �ڤ�Ĥ���ͤˤ��Ƥߤޤ����� + ���Ĥ��Ǥ� displayBuffer() �Υ����ɤ������� + ��HTML �椫�� title ������������ζ���ʸ���Ϻ�������ͤˤ��ޤ����� + ��[w3m-dev 01503], [w3m-dev 01504] �η�ν��� + buffername �Ͼ�� cleaup_str ������ݻ�����Ƥ��ޤ��� + �ʤ����������Ǥϡ�SJIS �Υե�����̾����ĥե�������ɤ�ȡ� + buffername �� URL �� SJIS �ˤʤäư����뤳�Ȥ����뤫�⤷��ޤ��� + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01646] putAnchor + HTML �Υ�������®�٤Υ٥���ޡ����Ƥߤ褦�Ȼפäơ��������� + ��äƤ�ȡ����륵����������®�٤��㲼���뤳�Ȥ�����ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01647] Re: first body with -m + �京���� #label �Ĥ��� URL ����ĥХåե��� reload ����ȡ� + ����������֤�����Ƥ��ޤ���礬����Ȥλ�Ŧ������ޤ����Τǡ� + ���� patch �Ǥ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01651] display column position with LINE_INFO + LINE_INFO(Ctrl-g) �ǥ������֤���Ϥ����ͤˤ��Ƥߤޤ����� + + +2001/1/5 + +From: Ryoji Kato <ryoji.kato@nrj.ericsson.se> +Subject: [w3m-dev 01582] rfc2732 patch + RFC2732 �˵��Ҥ���Ƥ���褦�� URL ��� '[' �� ']' �Ǥ�����줿 + literal IPv6 address ���᤹�롣 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01594] first body with -m (Re: w3m-m17n-0.7) + "-m" ���ץ�����Ĥ���ư�������Ȥ��ˡ����Υإå�����ʸ�δ֤� + ���Ԥν��������������Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01602] Re: first body with -m (Re: w3m-m17n-0.7) + ... + �ɤ����ˡ� + buf->lastLine->linenumber - buf->topLine->linenumber < LASTLINE - 1 + �Ȥ��������ä���Ȥ����Τ��ʡ� + �Ȥ����櫓�� patch ���äƤߤ��ΤǤ���������äȼ���̵���Ǥ��� + �ʤ���pgFore, pgBack �Υ���������֤��������������('J', 'K') + ��Ʊ��ư��ˤ��Ƥ��ޤ������ʤ�� �ؿ� SPC�٤ȡؿ� J�� ��Ʊ���� + vi ��ư��ȤϤ��ä�����äƤ�Ϥ��Ǥ������ɤ��Ǥ��礦�� + �Ĥ��Ǥˡ�reload, edit ���˥���������֤���¸�����������ɤ��Ƥ��ޤ��� + + +2001/1/1 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01584] Re: attribute replacing in frames. (Re: some fixes) + �⤦���١�frame���tag������ñ��ˤ���patch������ޤ��� + + +2000/12/27 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ե�����ν����˶��Ԥ�;ʬ���ɲä��������ν���. + + +2000/12/26 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01560] Re: long URL + >> ���ĤǤ��� + >> PEEK �� PEEK_LINK �Dz��������Ĺ�� URL ����褦�ˡ�prefix ������ + >> ���Ƽ������Ƥߤޤ����� + >> �����ϰ��٤�����ɽ���������ä��ΤǤ�������������ޤ�꤬�褯�狼��ʤ��� + >> ���Τǡ��Ȥꤢ����ɽ����������ʬ����ꤹ����ˡ��ȤäƤ��ޤ���2c �� 3u + >> �����Ϥ���ȡ����ꤵ�줿��ʬ���б����롢URL �ΰ�����ɽ������ޤ��� + >> ��ո����洶�ۤ��Ԥ����Ƥ���ޤ��� + ���������ΤϤɤ��Ǥ��礦�� + Ϣ³���� 'u' �� 'c' �� URL ����ʸ�����ĥ��������뤷�ޤ��� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01570] Re: long URL + ���ܤ���> # ���Ĥ���ΰƤ�����Ƥ⤤�����⤷��ޤ��� + + ���ܤ���� [w3m-dev 1560] ����κ�ʬ��ź�դ��ޤ�������Ĺ�� URL �ξ� + ���ͭ�����Ȼפ��ޤ�(���ޤ���פϤʤ������Ǥ���)�� + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01506] compile option of gc.a + NO_DEBUGGING ���դ��� gc.a ��ѥ��뤹��ȡ�gc.a �� w3m �ΥХ� + �ʥꥵ������¿���Ǥ����������ʤ�ޤ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01509] Forward: Bug#79689: No way to view information on SSL certificates + ���ɥ�����Ȥξ����ɽ��('=')�Ǹ��Ƥ� SSL�˴ؤ������������ + �ߤ��ʤ��ΤϳΤ����ᤷ���ʤ� �ȻפäƤ����Τ� Ŭ���ʥѥå� + �Ĥ��äƤߤޤ�����(���ʤꤤ��������) + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01556] Re: ANSI color support (was Re: w3m-m17n-0.4) + ANSI color support. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01535] how to check wait3 in configure. +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01537] Re: how to check wait3 in configure. + BSD/OS 3.1, SunOS 4.1.3 ��, configure �� wait3() �ФǤ��ʤ��� + ��ؤ��н�. + + +2000/12/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + <plaintext> ���ޤȤ��ư���Ƥ��ʤ��ä�����ν���. + + +2000/12/22 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01555] Re: some fixes for <select> + <option> �ʤ��� <select> �������������ͤˤ��Ƥ��ޤäƤ��ޤ����� + �ǽ����Ǥ��� + + +2000/12/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��feed_table �Υȡ�����ʬ������� HTMLlineproc0 �ǹԤʤ��褦���ѹ� + ����. + ��HTMLlineproc0 �Υե�����ν�����ᥤ��롼�פǹԤʤ��褦���ѹ��� + ��. + ��table �� <xmp> �� </xmp> �δ֤ˤ��륿�����ä��������������ν� + ��. + ���ե�����Υǡ��������������ɤ��ޤޤ���������Τ�, ����. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01536] Re: <P> in <DL> +Subject: [w3m-dev 01544] Re: <P> in <DL> + ����Τ��� HTML ��, �۾ェλ���������������ؤ��н�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + <a>, <img_alt>, <b>, <u> ���Υ������Ĥ��Ƥ��ʤ��Ȥ�, ��λ�������� + ������褦�ˤ���. + + +2000/12/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ʲ��ΥХ���ե���������. + ��feed_table_tag �� <dt> �����ν������������������ä�. + ��table ��ǥ������Ĥ��Ƥ��ʤ����, �۾ェλ����������ä�. + �ޤ�, <dt> ����ľ��� <p> ��̵�뤹��褦�ˤ���. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01530] returned at a morment. + read_token �� " �ǰϤޤ줿°���ͤν����Dz��Ԥ��åפ��Ƥ��ʤ��� + ���Х��ν���. +Subject: [w3m-dev 01531] coocie check in header from stdin. + cat ��� | w3m -m + �Ȥ��������ޤ��� + + +2000/12/17 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01513] Re: w3m-0.1.11-pre-kokb23 + frame.c �˥Х��Ȼפ���ս꤬����ޤ����� +Subject: [w3m-dev 01515] some fixes for <select> +Subject: [w3m-dev 01516] Re: some fixes for <select> + <select>��<option> �˴ؤ��ƴ��Ĥ��β��ɤ�Ԥ��ޤ����� + ��multiple °�����������Ƥ������ #undef MENU_SELECT �ξ��� + <option> �� value °�������ꤵ��Ƥ��ʤ��� form �Ȥ��Ƥ� + �ͤ������ʤ��Х��ν����� + ��<option> �� label °���ؤ��б��� + ���ǥե���Ȥ� name °�����ͤ� "default" �Ǥ���Τ� <input> �ʤɤ� + ��碌�� "" �ˡ� + ��<option> �� label �� "" �Ǥ����� "???" �ˤʤ�Τ�ߤ�� + # ����Ǥ��ä��ߤ�������ͤ����롣 + ��n_select >= MAX_SELECT �Ȥʤä���硢#undef MENU_SELECT �Υ����ɤ� + �Ȥ����ͤˤ����� + # MAX_SELECT = 100 �ʤΤǤޤ�̵��̣ + + +2000/12/14 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01501] Re: w3m-0.1.11-pre-kokb23 + no menu �ΤȤ��ˤҤȤĤ�������ѥ��륨�顼���Фޤ����Τǡ����ν��� + �ѥå��Ǥ��� + + +2000/12/13 + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01483] Patch to show image URL includes anchor + ����դ�������URL��Ȥ���`u'�Ǥϥ��URL���������ޤ��� + �Ǥ����������Υѥå���Ȥ���`i'�Dz������Τ�Τ�URL��������褦�� + �ʤ�ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01500] fix risky code in url.c + url.c �ˤ��ä��������Τ��륳���ɤ������ޤ����� + local.c �Ϥ��ޤ��ν����Ǥ��� + + +2000/12/12 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01491] bug ? + file.c �ΰʲ�����ʬ�Ǥ��������֤����Ȼפ��ޤ����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �̥�ʸ����ޤ�ʸ������Ф��븡�����Ǥ���褦�ˤ���. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01498] Re: null character + ̵�¥롼�פˤϤޤäƤ��ޤ��ޤ����� + + +2000/12/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��StrmyISgets ��, ñ�Ȥ� '\r' �����Ԥ�ǧ������ʤ��Х��ν���. + �ޤ�, ���ԥ����ɤ�ʥ�ʸ�����Ѵ��� cleanup_line ��ʬΥ����. + ���ڡ�����⡼�ɤ�, �ʥ�ʸ������褦�ˤ���. + ��base64 �� quoted printable �Υǥ����ɽ����� convertline ���� + istream.c �˰�ư. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01487] A string in <textarea> is broken after editing + w3m-0.1.11-pre-kokb21 �κ�����Ǥ�����<textarea> �����ʸ������� + �������ʸ������� ^` ���ͤ�ʸ�������뤳�Ȥ�����ޤ��� +Subject: [w3m-dev 01488] buffer overflow bugs + �Хåե��������С��ե������������������Τ���ʲ����������������ޤ����� + * file.c �� select_option[MAX_SELECT] ��ź���Υ����å���̵���ä��� + �� n_select �� MAX_SELECT ����� + * file.c �� textarea_str[MAX_TEXTAREA] ��ź���Υ����å����Դ������ä��� + �� n_textarea �� MAX_TEXTAREA ����� + * file.c �� form_stack[FORMSTACK_SIZE] ��ź���Υ����å���̵���ä��� + �� forms �˹�碌�� form_stack ��ݥ��Ȥ���ư��ĥ�����ͤˤ����� + * doFileCopy(), doFileSave() �� sprintf ��Ȥä� msg[LINELEN] �ؤ������� + �� Str msg �� Sprintf() ���֤������� + * local.c �� dirBuffer() �� sprintf ��Ȥä� fbuf[1024] �ؤ������� + �� Str fbuf ���֤������� + + +2000/12/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + maximum_table_width �� td, th ������ width °���ͤ��θ����褦�� + �ѹ�. + + +2000/12/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01473] Re: internal tag and attribute check + ������[w3m-dev 01446] �ǡ� + >> frame �����ɲä����°�� framename, referer, charset �ʤɤ� + >> ����ʤ��ΤǤ��礦��������Ū�˰��Ѥ�����ϻפ��դ��ޤ��� + �Ƚޤ�������<form charset=e> ���� w3m ����λ���Ƥ��ޤ��ޤ��� + accept-charset ��Ʊ�ͤǤ��Τǽ������ޤ����� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��table ������ hborder °�����̾�Ǥ�����դ���褦���ѹ�. + ��table ������ border °�����ͤ�Ϳ�����Ƥ��ʤ��Ȥ��ΰ������� + ����. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01478] Option Setting Panel + ��Ĺ�Υ�����ɥ��� Option Setting Panel ���ȡ��ֱ�Ӥ��� + �������б������Ť餤�Τǡ��֤�ͤ��ѥå��Ǥ��� + + +2000/12/7 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��parse_tag �� gethtmlcmd �ε�ǽ���������褦�ˤ���. + ���ǽ�� parse_tag ������������°��������դ��ʤ��褦�ˤ���. + �ޤ�, ����°�����ޤޤ�����, ��������°����ޤޤʤ��褦�˥��� + ����ľ���褦�ˤ���. + ��visible_length �Ǥ����פʥ����β��Ϥ�ߤ. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01456] linein.c + m17n ����� feed back �Ǥ�����linein.c �� calcPosition() �١����� + ��ľ���ޤ����������� display.c �Ȥۤ�Ʊ�ͤǤ��� + Ĺ��ʸ������˥��֤䥳��ȥ�����ʸ�������äƤ��������������뤬 + ư���褦�ˤʤäƤ���Ȼפ��ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01457] cursor position on sumbit form + TAB������<input type="submit" �� value="OK">�ξ�˥���������ư�� + �����Ȥ��ΰ��֤�����Ƥ�������ؤ��н�. + + +2000/12/3 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01449] Re: Directory of private header of gc library. + popText (rpopText) �ǺǸ�����Ǥ���Ф�����ˤ��Υꥹ�Ȥ˥����� + �����褦�Ȥ���Ȱ۾ェλ���Ƥ��ޤ���������������Ф��뽤��. + + +2000/12/2 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ޤ�, image map ���Ȥ��ʤ����꤬�ĤäƤ����Τǽ���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �����ơ��֥� (MYCTYPE_MAP) �ˤ�ä�, ʸ����ʬ�ह��褦���ѹ�. + ����, latin1, ascii, internal character ��Ƚ�̤ˤ� INTCTYPE_MAP �� + �Ȥ��褦���ѹ�. + # ��̤Ȥ���ɬ��̵���ʤä� CTYPE_MAP �Ϻ������. + + +2000/12/1 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: Security hole in w3m (<input_alt type=file>) + ��HTMLlineproc1, HTMLtagproc1 ���ΰ����˥ե饰��������ơ� + ������������������Ȥ��ʤ��ͤˤ����� + ��map.c �� `<', `>' �����������Ȥ���Ƥ��ʤ��ä���ν����� +Subject: [w3m-dev 01432] Re: w3m-0.1.11-pre-kokb22 patch + �ޤ���������ȴ���꤬����ޤ�����patch ���դ��ޤ��� + ([w3m-dev 01431] �Ǥβ��Ĥ���λ�Ŧ�ؤν��������äƤޤ�) +Subject: [w3m-dev 01437] Re: w3m-0.1.11-pre-kokb22 patch + �������ƥ���Ϣ�ν����� image map ���Ȥ��ʤ��ʤ�����ؤ��н�. + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01415] Lineedit patch for kokb21 + Subject: [w3m-dev 00976] move & delete until /, &, or ? + ����Ƥ���URL�����ϵ�ǽ���ĥ����ѥå���w3m-0.1.11-pre-kokb21�Ѥ� + ��ľ���ޤ�����kokb20�Ǥ�ѥå�����������Ƥ��ޤ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���Ĥ���Υѥå� [w3m-dev 01427] �ͤ�, HTML �Хåե���ʸ���ζ� + ��������륳��ѥ��륪�ץ���� (ENABLE_REMOVE_TRAILINGSPACES) �� + �ɲä���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00301] Re: "w3m -h" outputs to stderr + w3m -h �ν������ stderr ���� stdout ���ѹ�. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01430] Re: w3m-0.1.11-pre-kokb22 patch + EWS4800(/usr/abiccs/bin/cc) �Υ���ѥ��륨�顼�ؤ��н�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��dumm_table ������ id °�����ϰϥ����å���ä���. + ��form_int ������ fid °�����ϰϥ����å���ä���. + ��table �����å��Υ����Хե����Υ����å���ä���. + + +2000/11/29 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01422] bpcmp in anchor.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01423] Re: bpcmp in anchor.c + ��®���Τ���δ��Ĥ��ν���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��checkType �ΥХ��ե���������Ӽ㴳�ι�®��. + �������� 2 �Х���ñ�̤ǰ����褦���ѹ�. + + +2000/11/28 + +From: Takenobu Sugiyama <sugiyama@ae.advantest.co.jp> +Subject: patch for cygwin + cygwin �Ǥ� ftp�����Ȥ���� download �Ǥ���, �ʲ��� patch���н�� + ���ޤ���. cygwin �Ǥ�, �ե������ open/close�� binary �⡼�ɤˤ� + �Ƥ����ʤ���, �������������꤬����褦�Ǥ�. + + +2000/11/27 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01401] Re: bugfix of display of control chars, merge of easy UTF-8 patch, etc. + ���ν������ɲäǤ�������ԤκǸ�˥���ȥ�����ʸ��������Ȳ��̥��� + ���̤���ʤ��ʤäơ�����ʸ����ɽ���Ǥ��ʤ��Х��ν����Ǥ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table �Υ�����ι�®��. + + +2000/11/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table �Υ�������� width °���ǻ��ꤷ����Τ�꾮�����ʤ�������� + ����ν���. + + +2000/11/24 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �ޤ��ե�������ɤ߹���Ǥʤ��Ȥ���, �ץ����쥹�ѡ���ž��®�٤�ɽ�� + ���ʤ��褦���ѹ�����. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01385] Re: w3m-0.1.11-pre-kokb20 patch + w3m-0111-utf8-kokb20 �Ǥ�����conv.c �ǰ�ս�ְ㤤�Ȼפ���Ȥ��� + ������ޤ����Τǡ��ѥå���ź�դ��ޤ����Ĥ��Ǥˡ�����ǥ�Ȥ䥳��� + ������� warning �ν�����������Ƥ���ޤ��� + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �����ޥ�ɥ饤��ǥ��ץ����������ѹ������Ȥ�, proxy �� cookie �� + �ѹ���ȿ�Ǥ���ʤ���ʬ�����ä�������Ф��뽤��. + ����������ե�������֤���Ȥ�, ���Υե���������Ƥ��ޤ� + ��������������Ф��뽤��. + + +2000/11/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��StrStream ���Ф��Ƥ�, ���� Str �ΤޤޥХåե��Ȥ������Ѥ���� + �����ѹ�. + ��get_ctype ��ޥ�������, �ơ��֥��Ȥä�Ƚ�Ǥ���褦�ˤ���. + ��menu.c ���֤��ͤ�����Ȱ��פ��Ƥ��ʤ��꤬���ä��Τǽ���. + + +2000/11/22 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��˹�®���Τ�����ѹ��Ǥ�. + ���ե������ɤ߹����˼����ǥХåե����Ԥʤ��褦�ˤ���. + ��conv.c �δؿ��� Str �١������ѹ�. + ��ǽ�ʸ¤�ʸ����Υ��ԡ���Ԥʤ�ʤ��褦�ˤ���. + ��checkType �ι�®��. + ������������ʸ����̵���Ȥ� cursorRight ��ư������꤬���ä��Τ�, + ��������. + �ޤ���Ԥ� LINELEN ��ۤ����Ȥ���, calcPosition ������γ��� + ���������ǽ��������Τǥ��������ѹ�. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01372] w3m sometimes uses the wrong mailcap entry + http://bugs.debian.org/77679 + �Ǥ�����mime type ��Ƚ�Ǥ� substring match �ˤʤäƤ뤫����� + �פ��ޤ��������ľ���ʤ��Ǥ��礦�� + + +2000/11/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��Ȥ�̵�� table �� table ����ˤ���Ȥ���, ���� table ��������� + ��ؤ��н�. + + +2000/11/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + gc6 �б�. + + +2000/11/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���Хåե������ζ���ʸ���� 0x80-0x9f �˳����Ƥ�褦���ѹ�����. + �����ܸ��ǤǤ�, �Хåե���Ǥ� �� 0xa0 ��ɽ�魯�ˤ���. + �����ܤ���δʰ� UTF-8 �Ǥ� UTF-8 �Ȥϴط�̵����ʬ�Υ����ɤ�ޡ��� + ����. + �ޤ��ǥХå��ΤȤ��������ʤΤ�, ���������ɤ�ʸ���ɤ˻��ꤹ�� + �����Ǥ���褦�ˤ���. + ��ɽ���Բ�ǽ�ΰ� (0x80-0xa0) �ˤ���ʸ���� \xxx �η���ɽ������褦 + �ˤ���. + ��Ϣ����, ���̥��եȻ���, ����ȥ�����ʸ�����ޤޤ�Ƥ����ɽ���� + ����Х������ä��Τǽ�������. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01354] minimize when #undef USE_GOPHER or USE_NNTP + #undef USE_GOPHER �� #undef USE_NNTP �Ȥ����Ȥ��ˡ���Ϣ���륳���ɤ��� + ����������ʤ��ʤ�褦���ѹ����Ƥߤޤ����� + + +2000/11/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �۾�ʼ��λ��Ȥ� getescapechar ���Ѥ��ͤ��֤�������������ؤ��н�. + + +2000/11/15 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��table ���Ȥ�������������Х��ν���. + ��DEL ʸ�����ޤ��֤���ǽ�ʶ���ʸ���Ȥ��ư����褦���ѹ���, �Хåե� + �����ζ���ʸ���� ���� DEL ���ѹ�. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01338] Re: Lynx patch for character encoding in form +Subject: [w3m-dev 01342] Re: Lynx patch for character encoding in form + form ������ accept-charset °��������դ���褦�ˤʤä�. + + +2000/11/14 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ���������Ȥ���Τ�˺��Ƥ���Ȼפ�����ʬ����. + ��cleanup_str, htmlquote_str ��, �⤷ (����) �������Ȥ���ɬ�פ�̵ + �����, ����ʸ����Τޤ��֤��褦�ˤ���. + + +2000/11/10 + +From: ���Ƿ <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01336] patch for Cygwin 1.1.x + Cygwin 1.1.x (�����餯 1.1.3 �ʹ�) �����Υѥå���������ޤ����� + Cygwin 1.x �ʹߤδĶ��ˤ����ơ� + ��ɸ��Υ��ȡ���ѥ��� /cygnus/cygwin-b20/H-i586-cygwin32 + �ʲ����ѹ����ʤ� + ��T_as,T_ae,T_ac ����ˤ���Τ�� + + +2000/11/8 + +From: Jan Nieuwenhuizen <janneke@gnu.org> +Subject: [w3m-dev-en 00189] [PATCH] w3m menu <select> search + Enable to search within popup menu. + + +2000/11/7 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01331] Re: form TEXT: + ����ʸ����ȥե���������ʸ����Υҥ��ȥ�ΰ��ܲ�. + + +2000/11/4 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ����������������ۤ���Ȥ�, �������Ȥϲ���������������褦�ˤ���. + + +2000/11/2 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01313] Re: SCM_NNTP + MARK_URL �� nntp: ��ޥå�����褦�ˤ��Ƥߤޤ���������ɽ���� gopher: + �Τ�Τԡ����������Ǥ��� + + +2000/10/31 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01310] Re: option select (Re: w3mmee-0.1.11p10) + gc�饤�֥��Υ��顼��å�������disp_message_nsec���̤��ƽ��Ϥ��� + �褦�ˤ���. + + +2000/10/30 + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01294] mouse no effect on blank page. + mouse�����w3m ��blank�ʥڥ�����ɽ�����Ƥ������mouse�ܥ��� + �������ʤ�(��ܥ�������ʤ��Τ��ĥ饤)�Τǽ������Ƥߤޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01295] Re: mouse no effect on blank page. + �ºݤ�������櫓�ǤϤʤ��ΤǤ������������Ƥ������������Ǥ��͡� + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 01297] Re: backword search bug report + [w3m-dev 01296] ����𤵤�Ƥ���, ����������Ф����н�. + > �������ʤ�Ǥ�����"aaaa" �� "��������" �Τ褦��Ʊ��ʸ����Ϣ³���Ƥ� + > ��Ȥ��� "a" �� "��" �� backword search ����ȡ�����������֤� 1 ʸ�� + > ����Ǥ��ޤ��褦�Ǥ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01298] Re: backword search bug report + backword search �� wrapped search ��ͭ���λ������ߤιԤθ����� + �Ǥ��ʤ��Х���ľ���ޤ����� +Subject: [w3m-dev 01299] Re: backword search bug report + ���ܸ������Ȥ��� 2byte�ܤȼ���ʸ���� 1byte�ܤȤǥޥå������� + ��ȡ� little endian �Ǥ�����ɽ�� [��-��] ����������ǽ���ʤ����ꡢ + �Ѹ��ǤǤ� latin1 ����꤯�����Ǥ��ʤ��ä�(�Ǥ�����)�����ľ���ޤ����� + + +2000/10/29 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��LESSOPEN ����Ѥ��뤫�ɤ����� Option Setting Panel ������� + ���ˤ��� (default �ϻ��Ѥ��ʤ�). + �����̥ե����뿭ĥ��Υƥ�ݥ��ե��������Ȥ��γ�ĥ�Ҥ�, ���� + �ե�����γ�ĥ�� (.Z, .gz, .bz2) ���������ʬ�������褦���� + ������. + ��gunzip_stream, save2tmp, visible_length �ι�®��. + + +2000/10/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ����ѥ����, �ե�����̾�䴰��Υ��������Ƥ� Emacs-like �ˤǤ���� + ���ˤ���. + (config.h �� #define EMACS_LIKE_LINEEDIT �ˤ��ޤ�) + �ޤ�, �䴰����������˥Хå�������������ǽ�ˤ���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01284] Re: improvement of filename input + ��URL���ϻ�(U)�Ǥ� file:/ ����Ϥ����Τߥե�����̾�䴰��ͭ�� + �ˤ��ޤ����� + (URL ���Ϥλ��;夳��ʳ��Ǥϳμ¤� local-file �ˤʤ�ʤ�����) + ����������Υ��ɥХ����ˤ�� CTRL-D �Ǥΰ���ɽ���ϡ� + ʸ����κǸ�˥������뤬������˸��ꤷ�ޤ����� + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01280] Stop to prepend rc_dir to full path. + rcFile()�ե�ѥ��ˤ�rc_dir���դ��ʤ��褦����ѥå��Ǥ��� + + +2000/10/27 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01269] Re: SCM_NNTP + [w3m-dev 1258] �Ǻ��ܤ���Ŧ����Ƥ����Ȥ����������Ƥߤޤ������ѥ� + ����ź�դ��ޤ�����δĶ��Ǥϡ����ν����ʤ��� news:<Message-ID> �� + ư���ޤ���Ǥ����� +Subject: [w3m-dev 01273] Re: SCM_NNTP + url.c �������ơ�#undef USE_GOPHER �� #undef USE_NNTP �ΤȤ��ˤ� + gopther: �� news: ��ư��ʤ��褦�ˤ��ޤ������ޤ���nntp: ��ư��ʤ� + �褦�ˤ��ޤ����� + �ä��ơ�GOTO URL �� mailto: �����Ϥ����Ȥ���ư���褦���ѹ����Ƥߤ� + �������Ĥ��Ǥˡ������Ȥδְ㤤��ľ���Ƥ���ޤ��� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01258] improvement of filename input + �Dz��Ԥǥե�����̾�����Ϥ�����ζ�����Ԥ��ޤ����� + ��Ctrl-D ���䴰����ΰ�����ɽ������褦�ˤ��ޤ����� + ���̤������ڤ�ʤ�����Ϣ³���� Ctrl-D �Ǽ��θ���ΰ������Фޤ��� + # ʸ���κ���� BackSpace �� Del ��ȤäƤ��������� + ��URL ���ϻ�(GOTO)��ʸ���� file:/, file:/// �� file://localhost/ ���� + �ϤޤäƤ�����ϡ��ե�����̾���䴰�����ͤˤ��ޤ�����(���Ť������˾) + # http: �� ftp: �ϲ��⤷�ޤ��ҥ��ȥ꤫����䴰�Ǥ������ɡ� + ��URL ��ҥ��ȥ����¸������� password ��ʬ�Ϻ�������ͤ˽������ޤ����� + �ʤ����������餢�� undocument �ʵ�ǽ�Ǥ���������ʸ�������Ϥʤɤξ��Ǥ⡢ + Ctrl-X �� TAB(Ctrl-I) �Ǥ� �ե�����̾�䴰��ͭ���ˤʤ�ޤ��� + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01277] Accept-Encoding: gzip (Re: some wishlists) + Accept-Encoding: gzip, compress + ��ꥯ�����ȥإå����դ���褦�ˤ���. +Subject: [w3m-dev 01275] Re: squeeze multiple blank lines option ( http://bugs.debian.org/75527 ) + �Ȥꤢ���� #ifdef DEBIAN �� + squeeze multiple blank line �� -s + ü��ʸ�������ɻ���� -s/-e/-j �ϥʥ��������� -o kanjicode={S,E,J} ��Ȥ� + ���Ȥˤ��Ƥ����ޤ��� +Subject: [w3m-dev 01274] Re: SCM_NNTP + ���ä����ʤΤ� nntp: �ݡ��Ȥ��Ƥߤޤ��� +Subject: [w3m-dev 01276] URL in w3m -v + LANG=EN (�Ȥ����� undef JP_CHARSET)�λ��� visual mode �ǻȤ��Ƥ� URL + ���������ʤ��褦�Ǥ��� + + +2000/10/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + mailcap �� mime.type �ե�����ξ��� Option Setting Panel ������ + ��ǽ�ˤ���. + + +2000/10/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01247] Re: buffer selection menu + ��˥塼��Ϣ�� patch ����ӻ����ѹ� [w3m-dev 01227], [w3m-dev 01228], + [w3m-dev 01229], [w3m-dev 01237], [w3m-dev 01238] ��ޤȤ�ޤ����� + ��Select ��˥塼�Ǥξõ�(������ 'D') + ��Select ��˥塼�ǤΥ����Ȥ�ɽ�� + ��--- SPC for select / D for delete buffer ---�� + ������������������������������������������������ + ����˥塼����Υ��ޥ�ɼ¹Ԥ���ġ� + + +2000/10/24 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� ���å����������, `.' �����ƤΥɥᥤ���ɽ�魯�褦�ˤ���. + �� bm2menu.pl �� CVS �� add ����Τ�˺��Ƥ����Τ�, �ɲ�. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01240] Re: w3m-0.1.11-pre-kokb17 patch + �Ȥꤢ��������ѥ������ incompatible pointer type �Ȥ���줿�Ȥ� + ���ν����ѥå���ź�դ��ޤ��� + + +2000/10/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� ���ץ��������ѥͥ��, ���å���������դ��� (�����դ��ʤ�) �� + �ᥤ�������Ǥ���褦�ˤ���. + �ޤ�, ���å���������ĤΥ��������Ȥ���ʬΥ����. + �� frame �� reload �κ�, �ץ������Υ���å��夬��������Ƥ��ʤ��� + ������ؤ��н�. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01211] Re: a small change to linein.c +Subject: [w3m-dev 01214] Re: a small change to linein.c + Ĺ��ʸ������Խ������, ���Ƥ�ʸ����ɽ������ʤ�������������ؤ� + �н�. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01216] error message for invalid keymap +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01220] Re: error message for invalid keymap + keymap �����꤬���ä��Ȥ���, ���顼��å�������Ф��褦�˽���. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01217] keymap.lynx example could be better. + keymap.lynx �ι���. + + +2000/10/20 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + cookie �μ�갷���˴ؤ��ƴ��Ĥ��ν�����ä���. + �� version 1 cookie ���Ф��밷���� + http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt + �˽��褦���ѹ�. + Netscape-style cookie �Υꥯ�����ȥإå���, Cookie2 ���ɲ�. + �� [w3m-dev-en 00190] patch ���Ф�����Ĥ����ѹ�. + + +2000/10/19 + +From: "Ambrose Li [EDP]" <acli@mingpaoxpress.com> +Subject: [w3m-dev-en 00136] version 0 cookies and some odds and ends +Subject: [w3m-dev-en 00191] sorry, the last patch was not made properly +Subject: [w3m-dev-en 00190] w3m-0.1.10 patch (mostly version 0 cookie handling) + I've hacked up a big mess (patch) against w3m-0.1.9 primarily + involving version 0 cookies. To my dismay, it seems that most + servers out there still want version 0 cookies and version 0 + cookie handling behaviour, and w3m's cookie handling is too + strict for version 0, causing some sites (notably my.yahoo.co.jp) + not to work. + +2000/10/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ʸ�����������ǽ�ˤ���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01208] '#', '?' in ftp:/.... + ftp:/ �ǥե�����̾�� '#' �����äƤ���ȥ��������Ǥ��ʤ�����ؤ��� + ��. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01209] http_response_code and ``Location:'' header + ��Location:�ץإå�������ȡ�̵���ˤ���˽����褦�ˤʤäƤޤ����� + http_response_code��301��303�λ����������褦�ˤ��Ƥߤޤ����� + + +2000/10/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + local CGI ��, ����Ӥ��Ǥ�������ؤ��н�. + + +2000/10/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + table ��� <textarea> ���Ĥ��Ƥʤ���, ��λ�Ǥ��ʤ��ʤ�����ؤ��н�. + ([w3m-dev 00959] �����ذ�). + <select> �ΰ����˽स��褦�ˤ���. + +From: maeda@tokyo.pm.org +Subject: [w3m-dev 00990] auth password input + �����Ф���ѥ���ɤʤΤ��狼��ʤ��Τǡ��ʲ��Τ褦�� + �ѥå������Ƥޤ�����sleep(2)��Ĺ�����뤫�⡣ + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01193] Re: frame bug? + �ե졼��Τ���ڡ�������褷�Ƥ���Ȥ�, ����������������ؤ��н�. + + +2000/10/13 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00928] misdetection of IPv6 support on CYGWIN 1.1.2 + CYGWIN 1.1.2�ʹߤ�, ���ä� IPv6 ���ݡ��ȤФ��Ƥ��ޤ�����ؤ��� + ��. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01170] Re: cursor position after RELOAD, EDIT + ��cache �ե����뤬�Ĥ뤳�Ȥ�����Х��ν���. + ����¾ + ���ǥ��쥯�ȥ�ꥹ�Ȥ� URL �� /$LIB/dirlis.cgi�� �ȳʹ������ä��Τǡ� + ���Υǥ��쥯�ȥꤽ�Τ�Τˤʤ�褦�ˤ��ޤ����� + dirlist.in ���ѹ����Ƥ��ޤ��Τǡ�configure ��Ƽ¹Ԥ��뤫�� + cp dirlist.in dirlist.cgi �Ȥ��� @PERL@ �� @CYGWIN@ ������Ƥ��������� + ��keymap �ǰ����ҤǤ����ĥ��ʲ��δؿ���Ŭ�Ѥ��ޤ����� + LOAD �� �ե�����̾ + EXTERN, EXTERN_LINK �� �����֥饦��̾ + (w3m-control: ����ϻȤ��ޤ���) + EXEC_SHELL, READ_SHELL, PIPE_SHELL �� shell���ޥ�� + (w3m-control: ����ϻȤ��ޤ���) + SAVE, SAVE_IMAGE, SAVE_LINK, SAVE_SCREEN �� �ե�����̾(pipe ���ޥ��) + (w3m-control: ����ϻȤ��ޤ���) + + +2000/10/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + ��ɸ�����Ϥ���ΥХåե����ɤ߹���Ȥ�, MAN_PN �ƥХåե�̾���� + ��褦�ˤ���. + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01156] Re: w3m-0.1.11-pre-kokb15 + ��mydirname �ΥХ������ȴؿ�������ɲ� + ��SERVER_NAME �����ꤹ��褦���ѹ� + ��[w3m-dev-en 00234] �ͤ� GATEWAY_INTERFACE �����ꤹ��褦���ѹ� + ��current working directory ���ѹ����� popen ���롢���ޤȤ�ʼ��� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01158] some bugs fix when RELOAD, EDIT +Subject: [w3m-dev 01164] cursor position after RELOAD, EDIT + ��local CGI �Ȥ��ƸƤӽФ��� file:... �� EDIT �Ǥ���Х��������ޤ����� + # currentURL.scheme �ǤϤʤ� real_scheme ��Ȥ��褦�ˤ��ޤ����� + ��HTML ����ɽ�����֤��� RELOAD, EDIT ������ˤ� + ������ɽ�����֤ˤʤ�褦�ˤ��ޤ���(�����Զ�礬����ޤ���)�� + ���դ� plain text �ե������ HTML ɽ�����Ƥ�����֤��� RELOAD, EDIT + ������ˤ� HTML ɽ�����֤ˤʤ�褦�ˤ��ޤ����� + ��RELOAD, EDIT ��Υ���������֤� RELOAD, EDIT ����Ʊ���ˤʤ�褦�� + ���ޤ����� + + +2000/10/10 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01166] Re: cell width in table + table �ط��ΥХ��ե������Ǥ�. + �� ����������ʬ����ˤ�ؤ�餺, ʸ��������ޤ��֤���Ƥ��ޤ�����ν���. + �� table �� <wbr> �������ʤ�������������ν���. + �� feed_table_tag() �ν����ζ��̲�. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01155] history of data for <input type=text> + �դȻפ��Ф��� <input type=text> �����Ϥ����ǡ�����ҥ��ȥ�� + é����ͤˤ��Ƥߤޤ����� + ���������ӥ����Ϥ��⤯���ʤɤ������Ȼפ��ޤ��� + + +2000/10/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01150] Some bug fixes + [w3m-dev 00956] unknown scheme in frame + [w3m-dev 00975] goto link from frame page + ����𤵤줿����ν���. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01145] buffer overflow in linein.c + inputLineHist(linein.c) �ǥǥե����ʸ���� 256 ʸ���ʾ�ξ��� + strProp ���ΰ賰�����������뤳�Ȥ�����ޤ����Τǡ����ν��� patch �Ǥ��� + �ޤ�ʸ����Ĺ�������ͤ� 1024 �ˤ��ޤ����� + + +2000/10/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01136] function argument in keymap +Subject: [w3m-dev 01139] Re: function argument in keymap + Ĺ�餯����ˤʤäƤ� ~/.w3m/keymap �Ǥδؿ��ΰ���������ǽ�ˤ��ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01143] image map with popup menu + image map �� popup menu ��Ȥä� <option> ���ͤ�ɽ������褦�ˤ��Ƥߤޤ����� + config.h �� #define MENU_MAP �Ȥ��ƥ���ѥ��뤷�ƤߤƤ��������� + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 00971] Re: segmentation fault with http: + URL �Ȥ��� http: �� http:/ �����Ϥ��������Ƥ��ޤ��Τǽ������ޤ����� + + +2000/10/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01134] w3m in xterm horribly confused by Japanese in title (fr + http://bugs.debian.org/w3m ����𤵤�Ƥ���, �Ѹ��Ǥ����ܸ쥿���ȥ�� + ����ڡ������Ȥ���, w3m ��ȯ�������������������Ф���Х��ե����� + �Ǥ�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01127] SIGINT signal in ftp session (Re: my w3m support page) + ftp �κݤ� SIGINT ��ȯ������������Х��ν���. + + +2000/10/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + �� table �� recalc_width() �� wmin �κ����ͤ� 0.05 ���ѹ�. + �� �������ޥ�ɤν��ϥХåե��� filename, basename, type ���ѹ�. + �� http �� local file �ʳ��ΰ��̥ǡ�����Ĺ����Τ�, ��ö�ƥ�ݥ�� + �ե��������Ȥ��褦�ˤ���. + �� �ƥ�ݥ��ե�����̾������������ˡ���ѹ�. + �� mailcap �� edit= ���᤹��褦�ˤ���. + �� URLFile �ν�������Դ������ä�����ν���. + �� �ĤäƤ���������ѥå��Υ��ߤκ��. + + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + -dump, -source_dump ���ץ����β���, frame ��� <meta> ������̵�� + ����褦�ˤ�. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00930] HTML-quote in w3mbookmark.c + "�֥å��ޡ�������Ͽ" �� URL �� Title �� HTML-quote ����Ƥ��ʤ��Τ� + �������ޤ����� + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00972] better display of progress bar ? + 2Mb �Υե�������ɤ�Ǥ�����ˡ����ä� 0/2Mb �ˤʤä��ᤷ���ä��Τǡ� + �ץ����쥹�С���ɽ���� %.0f (%.1f) ���� %.3g �ˤ��Ƥߤ���Ǥ����� + �ɤ�ʤ��Ǥ��礦�� + + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + textlist ���Ф��� null pointer �����å���ä���. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01100] space in URL + + * http://bugs.debian.org/60825 �� http://bugs.debian.org/67466 + + form �� submit ������� value ���� form_quote() ���Ƥޤ��� + name ������ form_quote() ����ɬ�פ�����ޤ��� + + * http://bugs.debian.org/66887 + + Goto URL: ����Ƭ�� space ������� current��������а����ˤʤ�Τ� + ���Ƥۤ����Ȥ�����𡣤������� cut & paste ����Ȥ��ˤʤ꤬���ʤΤ� + (�Ĥ��ǤʤΤǸ���ζ������) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01111] bug of conv.c + UTF-8 �ʥڡ���(Shift_JIS �ȸ�ǧ�����)�� w3m ��ɽ�������� + (����ȥ����륷������ϳ���)���Ȥ����ä��Τ�Ĵ�٤Ƥߤ��Ȥ����� + conv.c ���Х��äƤޤ�����ñ��ߥ��Ǥ������ߤޤ���m(_o_)m + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01113] bug fix (content charset) + content charset ���������ǥХ��äƤޤ����Τǡ����� patch �Ǥ��� + + +2000/10/02 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01112] Re: mailcap test= directive + mailcap �ΰ������ĥ���ޤ���. + �� %s �ʳ���, %t (content-type name) ��Ȥ���褦�ˤ��ޤ���. + �� nametemplate ���ץ����ͭ���ˤʤ�ޤ���. + �� %s ��̵������, ɸ�����Ϥ� %s �˥�����쥯�Ȥ��ƥ��ޥ�ɤ�¹Ԥ� + ��褦�ˤ��ޤ���. + ������ι�ʸ�Ȥ��ƥܡ�������ꤷ�Ƥ���Τ�, OS/2 ���ǤϤ��� + �ޤޤǤ����ܤ��⤷��ޤ���. + �� needsterminal �����ꤵ��Ƥ������, �ե��������ɤǥ��ޥ�ɤ�� + �Ԥ���褦�ˤ��ޤ���. + �� copiousoutput �����ꤵ��Ƥ������, ���ޥ�ɤμ¹Է�̤�Хåե� + ���ɤ߹���褦�ˤ��ޤ���. + �� RFC 1524 �ˤ�̵���ΤǤ���, ���ޥ�ɤμ¹Է�̤� text/html �Ȥ��� + �Хåե����ɤ߹��ि��Υ��ץ���� htmloutput ���ɲä��ޤ���. + �����, ���ܤ��� [w3m-dev 01079] ����Ƥ���Ƥ�����Τ����ذƤ� + �Ĥ��Ǥ�. + �ޤ��ƥ��Ȥ��Ƥޤ���, ������ư���Ƥ���� + + application/excel; xlHtml %s | lv -Iu8 -Oej; htmloutput + + �Ȥ����, lv �μ¹Է�̤� html �Ȥ��� w3m �ΥХåե���ɽ������� + �Ϥ��Ǥ�. + Ʊ�� content-type �Υ���ȥ꤬ʣ��������, htmloutput ���ץ���� + �������Τ�ͥ�褹��褦�ˤ��Ƥ���Τ�, ¾�Υץ������� mailcap + ��ͭ���Ƥ�����̵���Ȼפ��ޤ�. + ������, RFC 1524 �˽�Ƥʤ��ΤϳΤ��ʤΤ�, ��ո����Ԥ����Ƥ� + ��. + �� (gunzip_stream() �ˤ��) ���̥ե�����α����� ftp ���Ф��Ƥ�Ȥ� + ��褦�ˤ��ޤ���. + ¿ʬ [w3m-dev 01078] �ΥХ����Ȼפ��ޤ���, http ���Ф���, ���̤� + ���ƥ����ȥǡ����α������Ǥ��ʤ��ʤäƤ��Τ�, �������ޤ���. + + +2000/09/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01097] gunzip_stream problem + ���̥ե�������ɤ߹���Ǥ������, INT �����ʥ뤬ȯ�������Ȥ���ư�� + ���ѤʤΤ�, �������ޤ���. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01092] CONFIG_FILE + config.h �� CONFIG_FILE ���ѹ����Ƥ�ȿ�Ǥ���ʤ��ʤȻפä��顢 + ���ĤΤޤˤ��ϡ��ɥ����ǥ�����äƤޤ����� + ���ν����Ǥ��� + + +2000/09/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01078] treatment of content type + document type �ΰ����β��ɤ�Ԥʤ��ޤ���. + �� examineFile �ˤ�����, lessopen_stream �� gunzip_stream ��ͥ���� + ���ѹ����ޤ���. + �� lessopen_stream �ν������, plain text �Ȥ��ư����褦�ˤ��ޤ���. + �� lessopen_stream ��, document type �� text/* �Ǥ��뤫, �����ӥ塼�� + �����ꤵ��Ƥ��ʤ����ΤȤ��褦�ˤ��ޤ���. + �ޤ�, text/html �ʳ���, text/* ���� w3m �����ǽ�������褦�ˤ��� + ����. + �� page_info_panel ��ɽ������� document type ��, examineFile �ǽ��� + ����������ͤ�Ȥ��褦�ˤ��ޤ���. + �� �����ӥ塼����Хå������ɤ�ư�����Ȥ�, ���ޥ�ɥ饤��� + ">/dev/null 2>&1 &" ���դ��Ƥߤޤ���. + + +2000/09/13 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01053] Re: Location: in local cgi. + [w3m-dev 01051] �Υѥå��Ǥϡ�w3m -m �� Location: �Υإå��Τ���ʸ�Ϥ� + ���������Ǥ��äƤ��ޤ��Τǡ�local CGI �ΤȤ��Τ� Location: �Ȥ� + ��褦���ѹ����ޤ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01065] map key '0' + keymap ����ߤν����Ǥ��� + ��ñ�Ȥ� '0' ���ޥåײ�ǽ�ˤ��ޤ����� + ��10 j�٤Ȥ��ϰ����̤�Ǥ��� + ����ESC ���٤ʤ� ESC �θ�� 0x80-0xff ��ʸ�������Ϥ���� + �������������ǽ�������ä���Τ����� + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01066] 104japan + frame ��� form ��ʸ�������ɤ��Ѵ�����꤯�����Ǥ��Ƥ��ʤ��褦 + �Ǥ��Τǡ��������ޤ����� + + +2000/09/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01058] <dt>, <dd>, <blockquote> (Re: <ol> etc.) + �� <blockquote> ������ζ��ԤϾ������褦�ˤ���. + �� <dt>, <dd> ����ľ��� <p> ������̵�뤷�ʤ��褦�ˤ���. + + +2000/09/04 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01052] cellpadding, cellspacing, vspace, etc. + �������Ԥ˴ؤ���, ���Τ褦�ʤ����Ĥ����ѹ���Ԥʤ��ޤ���. + �� ;ʬ�ʥ��뤬�����Τ��ɤ������, <tr> �� <td> �γ��ˤ��� + <a name="..."></a> ��, <font> ���ϼ��Υ��������褦�ˤ���. + �� <table> �� cellspacing °���β���ְ�äƤ����Τ�, ��������. + vspace °������Ǥ���褦�ˤ���. + �� ���Ԥ�Ƚ������ѹ�����. + �� </p> �����Ƕ��Ԥ�����褦�ˤ���. + + +2000/08/17 + +From: Tsutomu Okada (���� ��) <okada@furuno.co.jp> +Subject: [w3m-dev 01018] sqrt DOMAIN error in table.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01019] Re: sqrt DOMAIN error in table.c + �������Ȥ���ˤʤ��礬��������ν���. + + +2000/08/15 + +From: satodai@dog.intcul.tohoku.ac.jp (Dai Sato) +Subject: [w3m-dev 01017] value of input tag in option panel + aito Ϣ��Ģ��http://ei5nazha.yz.yamagata-u.ac.jp/BBS/spool/log.html�� + �˽ФƤ�����Ǥ���option ���̤γ��� editor �ʤɤ� '"' ���ޤޤ�� + ���ޥ�ɤ����ꤵ���ȡ����� option ���̤�ƤӽФ������� '"' �ʹߤ� + ɽ������ʤ��ʤ�ȸ������ꡣ + + +2000/08/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01016] Table geometry calculation + table �Υ�����ȥ���Ǽ¿��������˴ݤ������ѹ�����, table ���� + �����ͤ����������κ�����ǽ�ʸ¤꾮�����ʤ�褦�ˤ��Ƥߤޤ���. + + +2000/07/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01006] initialize PRNG of openssl 0.9.5 or later + �С������ 0.9.5 �ʹߤ� openssl �饤�֥���, ������ǥХ��� + (/dev/urandom) ��¸�ߤ��ʤ��Ķ��Ǥ� SSL ���Ȥ���褦�ˤ��Ƥߤޤ���. + + +2000/07/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01004] unused socket is not closed. + C-c (SIGINT) �ǥե�������ɤ߹��ߤ����Ǥ����Ȥ�, socket �������������� + �Ƥ��ʤ���礬����褦�Ǥ�. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01005] table caption problem + </caption> ��˺��Ƥ����Ȥ��� w3m ����λ���ʤ��ʤ����������ν���. + + +2000/07/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00966] ssl and proxy authorization + authorization ��ɬ�פȤ������ HTTP proxy �����Ф� SSL �ȥ�ͥ� + �����꤬���ä��Τǽ���. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01003] Some bug fixes for table + table �Υ�����ȥ���Τ����Ĥ���������Ф��뽤��. + + +2000/07/16 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00999] Re: bookmark + �֥å��ޡ�������Ͽ�Ǥ��ʤ���礬��������ν���. + + +2000/06/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00934] clear_buffer bug + clear_buffer �� TRUE �ΤȤ�, selBuf() �Dz��̤��ä��Ƥ��ޤ�������Ф��� + �Х��ե������Ǥ�. + + +2000/06/17 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00929] ftp.c patch + USER ���ޥ�ɤ��Ф��� 230 ���֤äƤ������ˤ�����������Τ� + �ߤʤ� patch ��������ޤ������ʲ���ź�դ��ޤ��� + + +2000/06/16 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00923] some bug fixes + �� #undef JP_CHARSET �ξ��� file.c �� make �Ǥ��ʤ��ʤäƤ��� + �Х�(��Υߥ��Ǥ���_o_)�ν����ȡ� + �� buffer.c �� '=' �� '==' �ˤʤäƤ�����Τν����Ǥ��� + +From: Kazuhiko Izawa <izawa@nucef.tokai.jaeri.go.jp> +Subject: [w3m-dev 00924] Re: w3m-0.1.11pre +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00925] Re: w3m-0.1.11pre + file://localhost/foo �η����� URL �˥����������褦�Ȥ����Ȥ��۾ェ + λ���Ƥ��ޤ�����ν���. diff --git a/doc-jp/MANUAL.html b/doc-jp/MANUAL.html new file mode 100644 index 0000000..b6aae96 --- /dev/null +++ b/doc-jp/MANUAL.html @@ -0,0 +1,524 @@ +<html> +<head><title>w3m manual</title> +</head> +<body> +<h1>w3m �ޥ˥奢��</h1> +<div align=right> +��ƣ ��§<br> +aito@ei5sun.yz.yamagata-u.ac.jp +</div> +<h2>�⤯��</h2> +<menu> +<li><a href="#Introduction">�Ϥ����</a> +<li><a href="#Options">��ư���ץ����</a> +<li><a href="#Color">ʸ���ɽ������</a> +<li><a href="#Key:orig">��ư��λȤ�����(���ꥸ�ʥ�)</a> +<li><a href="#Key:lynx">��ư��λȤ�����(Lynx��)</a> +<li><a href="#Mouse">�ޥ������</a> +<li><a href="#Key:custom">���������</a> +<li><a href="#LocalCGI">Local CGI</a> +</menu> +<hr> +<a name="Introduction"></a> +<h2>�Ϥ����</h2> +w3m �ϡ��ƥ����ȥ١����Υڡ�����/WWW�֥饦���Ǥ��������Ȥ��ȡ�kterm �ʤɤΥ���饯�� +ü����ǡ���������ե�������ꡤWWW�����Ƥ��ꤹ�뤳�Ȥ��Ǥ��ޤ��� + +<hr> +<a name="Options"></a> +<h2>��ư���ץ����</h2> + +��ư���ΰ����ϼ����̤�Ǥ��� +<p> +<pre> + w3m [options] [file|URL] +</pre> +<P> +�����˥ե�����̾����ꤹ��Ф��Υե������ɽ������URL����ꤹ��Ф������Ƥ�ɽ�����ޤ��� +������ꤷ�ʤ���С�ɸ�����Ϥ����Ƥ�ɽ�����ޤ�����������ɸ�����Ϥ� tty �Ǥ�����ˤϡ� +���⤻���˽�λ���ޤ��� +<p> +���ץ����ϼ����̤�Ǥ��� +<dl> +<dt>+�ֹ� +<dd>��ư�塤����ι��ֹ�˰�ư���롥 +<dt>-t �� +<dd>���֤�������ꤹ�롥�ǥե���Ȥ� 8 �� +<dt>-r +<dd>text/plain ��ʸ���ɽ�������硤�Ť��Ǥ��ˤ�붯Ĵʸ����ɽ�����ʤ��� +���Υ��ץ������դ��ʤ���硤``A^H_''��A�Υ�������饤��Ȥ���ɽ�����졤 +``A^HA''��A�Υܡ���ɤȤ���ɽ������롥 +<dt>-S +<dd>text/plain ��ʸ���ɽ�������硤ʣ���ζ��Ԥ�1�ĤˤޤȤ��ɽ�����롥 +<dt>-l �Կ� +<dd>ɸ�����Ϥ����Ƥ�ɽ������Ȥ�����¸��������Կ�����ꤹ +�롥�ǥե���Ȥ� 10000�� +<dt>-s +<dd>Shift_JIS �����ɤ�ɽ�����롥 +<dt>-e +<dd>EUC �����ɤ�ɽ�����롥 +<dt>-j +<dd>JIS(ISO-2022-JP) �����ɤ�ɽ�����롥 +<dt>-T ������ +<dd>ɽ������ʸ��Υ����פ���ꤹ�롥���λ��꤬�ʤ���硤�ե����� +̾�γ�ĥ�Ҥˤ�äƼ�ưȽ�̤���롥Ƚ�̤Ǥ��ʤ����� text/plain +�Ȥߤʤ���롥<p> +�㡧<br> +ɸ�����Ϥ��� HTML �ե�������ɤ��ɽ������ +<pre> + cat hoge.html | w3m -T text/html +</pre> +<p> +HTML�ե�����Υ�������ɽ������ +<pre> + w3m -T text/plain hoge.html +</pre> +<dt>-m +<dd>Internet message �⡼�ɤ�ɽ�����롥Internet message�⡼�ɤξ�硤 +�إå������Ƥơ�Content-Type: ������Ф���ͤˤ��롥�Żҥ��� +�ͥåȥ˥塼���ε������ɤ�Ȥ��������� +<dt>-B +<dd>Bookmark ��ɽ�����롥 +<dt>-bookmark file +<dd>Bookmark�Υե��������ꤹ�롥 +<dt>-M +<dd>���顼ɽ���ʤ��� +<dt>-F +<dd>�ե졼���ưɽ�����롥 +<dt>-S +<dd>Ϣ³������Ԥ�1�ԤˤޤȤ��ɽ�����롥 +<dt>-X +<dd>w3m��λ���ˡ������β��̤����ʤ��� +<dt>-W +<dd>�ޤ��֤���������Ȥ����ɤ������ڤ꤫���롥 +<dt>-o option=value +<dd>���ץ�������ꤹ�롥 +<dt>-no-proxy +<dd>�ץ����������Ѥ��ʤ��� +<dt>-no-mouse +<dd>�ޥ��������Ѥ��ʤ��� +<dt>-cookie +<dd>���å�����������롥 +<dt>-no-cookie +<dd>���å�����������ʤ��� +<dt>-num +<dd>���ֹ��ɽ�����롥 +<dt>-dump +<dd>URL�����Ƥ��ɤߤ��ߡ��������줿�Хåե������Ƥ�ɸ����Ϥ˽Ф��� +ʸ�������80��Ȳ��ꤵ��롥�������ϡ����� -cols ���ץ������ѹ���ǽ�� +<dt>-cols �� +<dd>-dump ���ץ�����Ȥ����ˡ�ʸ���������ꤹ�롥 +<dt>-ppc �ԥ������ +<dd>ʸ����������ꤹ�롥�ǥե���Ȥ� 8.0�� +<dt>-dump_source +<dd>URL�����Ƥ��ɤߤ��ߡ�����������ɸ����Ϥ˽Ф��� +�����������Ѵ��⤵��ʤ��� +<dt>-dump_head +<dd>URL�˥������������إå��������Ϥ��롥 +</dl> + +<hr> +<a name="Color"></a> +<h2>ʸ���ɽ������</h2> + +HTMLʸ���ɽ�����Ƥ���Ȥ��ˤϡ����Τ褦��ɽ���ˤʤ�ޤ��� +<div align="center"> +<table border="1"> +<tr><th></th><th>���顼ɽ����</th><th>���ɽ����</th></tr> +<tr><td>���</td><td>�Ŀ�</td><td>����</td></tr> +<tr><td>����饤�����</td><td>�п�</td><td>ȿžɽ��</td></tr> +<tr><td>FORM��������ʬ</td><td>�ֿ�</td><td>ȿžɽ��</td></tr> +</table> +</div> +���顼ɽ�����ο��ϡ����ץ��������ѥͥ� "o" ���ѹ����뤳�Ȥ��Ǥ��ޤ��� + +<hr> +<a name="Key:orig"></a> +<h2>��ư��λȤ�����(���ꥸ�ʥ�)</h2> + +��ư������ϡ�1ʸ���Υ��ޥ�ɤ��ܡ��ɤ������Ϥ��뤳�Ȥ�w3m�����ޤ��� +���ޥ�ɤˤϼ��Τ褦�ʤ�Τ�����ޤ����ʲ��ε��ҤǤϡ�C-x �ϥ���ȥ�����x +��ɽ���ޤ����ޤ���SPC �ϥ��ڡ����С���RET �ϥ������ESC �ϥ��������ץ����Ǥ��� +<P> +�����ǽƤ���Τϡ����ꥸ�ʥ��ǤΥ������Ǥ���Lynx���Υ�������Ѥ� +����ѥ��뤷�Ƥ����ΤˤĤ��Ƥϡ�<a href="#Key:lynx">��ư��λȤ���(Lynx��)</a> +��������� + +<H3>�ڡ���/���������ư</H3> +<table> +<TR><TD>SPC,C-v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>b,ESC v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>l,C-f,���������<TD>��������˰�ư���ޤ��� +<TR><TD>h,C-b,���������<TD>��������˰�ư���ޤ��� +<TR><TD>j,C-n,���������<TD>��������˰�ư���ޤ��� +<TR><TD>k,C-p,���������<TD>����������˰�ư���ޤ��� +<TR><TD>J<TD>���̤�1�Ծ�˥��������뤷�ޤ��� +<TR><TD>K<TD>���̤�1�Բ��˥��������뤷�ޤ��� +<TR><TD>w<TD>����ñ��˰�ư���ޤ��� +<TR><TD>W<TD>����ñ��˰�ư���ޤ��� +<TR><TD>><TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD><<TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD>.<TD>�������Τ�1ʸ�����ˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD>,<TD>�������Τ�1ʸ�����ˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD>g<TD>ʸ��Τ����Ф��ιԤ˰�ư���ޤ��� +<TR><TD>G<TD>ʸ��Τ����ФιԤ˰�ư���ޤ��� +<TR><TD>ESC g<TD>���̲��ǹ��ֹ�����Ϥ��������ǻ��ꤷ���Ԥ˰�ư���ޤ��� +������ $ �����Ϥ���ȡ��ǽ��Ԥ˰�ư���ޤ��� +<TR><TD>TAB<TD>���Υ�˰�ư���ޤ��� +<TR><TD>C-u, ESC TAB<TD>���Υ�˰�ư���ޤ��� +<TR><TD>[<TD>�ǽ�Υ�˰�ư���ޤ��� +<TR><TD>]<TD>�Ǹ�Υ�˰�ư���ޤ��� +</table> + +<H3>�ϥ��ѡ�������</H3> +<table> +<TR><TD WIDTH=100>RET<TD>���ߥ������뤬�������ؤ����ʸ����ɤߤ��ߤޤ��� +<TR><TD>a, ESC RET<TD>���ߥ������뤬�������ؤ����ʸ���ե��������¸���ޤ��� +<TR><TD>u<TD>���ߥ������뤬�������ؤ����URL��ɽ�����ޤ��� +<TR><TD>I<TD>���ߥ������뤬�������б����������ɽ�����ޤ��� +<TR><TD>ESC I<TD>���ߥ������뤬�������ؤ� +������ե��������¸���ޤ��� +<TR><TD>:<TD>URL����ʸ������ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>ESC :<TD>Message-ID����ʸ�����news: �Υ�ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>c<TD>���ߤ�ʸ���URL��ɽ�����ޤ��� +<TR><TD>=<TD>���ߤ�ʸ��˴ؤ�������ɽ�����ޤ��� +<TR><TD>F<TD><FRAMESET>��ޤ�ʸ���ɽ�����Ƥ���Ȥ��ˡ�<FRAME> +�����λؤ�ʣ����ʸ���1�Ĥ�ʸ����Ѵ�����ɽ�����ޤ��� +<TR><TD>M<TD>���߸��Ƥ���ڡ��������֥饦����Ȥä�ɽ�����ޤ��� +2M, 3M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +<TR><TD>ESC M<TD>���ߤΥ��������֥饦����Ȥä�ɽ�����ޤ��� +2ESC M, 3ESC M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +</table> + +<H3>�ե������URL�ط������</H3> +<table> +<TR><TD WIDTH=100>U<TD>URL����ꤷ�Ƴ����ޤ��� +<TR><TD>V<TD>��������ե��������ꤷ�Ƴ����ޤ��� +<TR><TD>@<TD>���ޥ�ɤ�¹Ԥ�����̤������ɤ�Ǥ���ɽ�����ޤ��� +<TR><TD>#<TD>���ޥ�ɤ�¹Ԥ�����̤��ɤߤ��ߤʤ���ɽ�����ޤ��� +</table> + +<H3>�Хåե����</H3> +<table> +<TR><TD WIDTH=100>B<TD>���߸��Ƥ���Хåե���������������ΥХåե���ɽ�����ޤ��� +<TR><TD>v<TD>HTML�Υ�������ɽ�����ޤ��� +<TR><TD>s<TD>�Хåե�����⡼�ɤ�����ޤ��� +<TR><TD>E<TD>���߸��Ƥ���Хåե�����������ե�����ξ�硤���Υե�����ǥ��� +���Խ����ޤ������ǥ�����λ�����塤���Υե����������ɤ߹��ߤޤ��� +<TR><TD>R<TD>�Хåե�������ɤ߹��ߤޤ��� +<TR><TD>S<TD>�Хåե���ɽ�����Ƥ�ե��������¸���ޤ��� +<TR><TD>ESC s<TD>HTML�Υ�������ե��������¸���ޤ���v �ǥ�������ɽ������ S �� +��¸����ΤȤۤ�Ʊ���Ǥ�����ESC s ����¸�����ե�����ϴ��������ɤ����ꥸ�ʥ�� +�ޤޤǤ���Τ��Ф��ơ�v S ����¸����ȸ���ɽ���˻ȤäƤ�����������ɤ��Ѵ����� +����¸����ޤ��� +<TR><TD>ESC e<TD>����ɽ������Ƥ���Хåե���ɽ������Ƥ�������Τޤ� +���ǥ������Խ����ޤ��� +</table> + +<H3>�Хåե�����⡼��</H3> +"s" �ǥХåե�����⡼�ɤ����ä��Ȥ��Υ������Ǥ��� +<table> +<TR><TD WIDTH=100>k,C-p<TD>��ľ�ΥХåե������ޤ��� +<TR><TD>j,C-n<TD>��IJ��ΥХåե������ޤ��� +<TR><TD>D<TD>�������Ƥ���Хåե��������ޤ��� +<TR><TD>RET<TD>�������Ƥ���Хåե���ɽ�����ޤ��� +</table> + +<H3>�֥å��ޡ������</H3> +<table> +<TR><TD WIDTH=100>ESC b<TD>�֥å��ޡ������ɤ߹��ߤޤ��� +<TR><TD>ESC a<TD>���߸��Ƥ���ڡ�����֥å��ޡ������ɲä��ޤ��� +</table> + +<H3>����</H3> +<table> +<TR><TD WIDTH=100>/,C-s<TD>���ߤΥ���������֤���ե����������˸����ä�����ɽ�������ޤ��� +<TR><TD>?,C-r<TD>���ߤΥ���������֤���ե��������Ƭ�˸����ä�����ɽ�������ޤ��� +<TR><TD>n<TD>�������ޤ��� +<TR><TD>N<TD>�������ޤ��� +</table> + +<H3>�ޡ������</H3> +<table> +<TR><TD WIDTH=100>C-SPC<TD>�ޡ��������������ޤ����ޡ�����ȿžɽ������ޤ��� +<TR><TD>ESC p<TD>������Υޡ����˰�ư���ޤ��� +<TR><TD>ESC n<TD>��ĸ�Υޡ����˰�ư���ޤ��� +<TR><TD>"<TD>����ɽ���ǻ��ꤵ�줿ʸ��������ƥޡ������ޤ��� +</table> + +<H3>����¾</H3> +<table> +<TR><TD WIDTH=100>!<TD>�����륳�ޥ�ɤ�¹Ԥ��ޤ��� +<TR><TD>H<TD>�إ�ץե������ɽ�����ޤ��� +<TR><TD>o<TD>���ץ��������ѥͥ��ɽ�����ޤ��� +<TR><TD>C-k<TD>���å���������ɽ�����ޤ��� +<TR><TD>C-c<TD>ʸ����ɤ߹��ߤ����Ǥ��ޤ��� +<TR><TD>C-z<TD>�����ڥ�� +<TR><TD>q<TD>w3m��λ���ޤ������ץ���������ˤ�äơ���λ���뤫�ɤ�����ǧ���ޤ��� +<TR><TD>Q<TD>��ǧ������w3m��λ���ޤ��� +</table> + +<H3>���Խ�</H3> +���̤κDz��Ԥ�ʸ��������Ϥ������ͭ���ʥ������Ǥ��� +<table> +<TR><TD WIDTH=100>C-f<TD>��������˰�ư���ޤ��� +<TR><TD>C-b<TD>��������˰�ư���ޤ��� +<TR><TD>C-h<TD>���������ľ����ʸ���������ޤ��� +<TR><TD>C-d<TD>����������֤�ʸ���������ޤ��� +<TR><TD>C-k<TD>����������֤����������ޤ��� +<TR><TD>C-u<TD>����������֤������������ޤ��� +<TR><TD>C-a<TD>ʸ�������Ƭ�˰�ư���ޤ��� +<TR><TD>C-e<TD>ʸ����κǸ�˰�ư���ޤ��� +<TR><TD>C-p<TD>�ҥ��ȥ꤫��������ʸ�������Ф��ޤ��� +<TR><TD>C-n<TD>�ҥ��ȥ꤫�鼡��ʸ�������Ф��ޤ��� +<TR><TD>TAB,SPC<TD>�ե�����̾���ϻ��ˡ��ե�����̾���䴰���ޤ��� +<TR><TD>RETURN<TD>���Ϥ�λ���ޤ��� +</table> + +<hr> +<a name="Key:lynx"></a> +<h2>��ư��λȤ�����(Lynx��)</h2> +Lynx�������Х���ɤǥ���ѥ��뤷�����λȤ������Ǥ��� +<H3>�ڡ���/���������ư</H3> +<table> +<TR><TD>SPC,C-v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>b,ESC v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>l<TD>��������˰�ư���ޤ��� +<TR><TD>h<TD>��������˰�ư���ޤ��� +<TR><TD>j<TD>��������˰�ư���ޤ��� +<TR><TD>k<TD>����������˰�ư���ޤ��� +<TR><TD>J<TD>���̤�1�Ծ�˥��������뤷�ޤ��� +<TR><TD>K<TD>���̤�1�Բ��˥��������뤷�ޤ��� +<TR><TD>><TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD><<TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD>C-a<TD>ʸ��Τ����Ф��ιԤ˰�ư���ޤ��� +<TR><TD>C-e<TD>ʸ��Τ����ФιԤ˰�ư���ޤ��� +<TR><TD>G<TD>���̲��ǹ��ֹ�����Ϥ��������ǻ��ꤷ���Ԥ˰�ư���ޤ��� +������ $ �����Ϥ���ȡ��ǽ��Ԥ˰�ư���ޤ��� +<TR><TD>TAB, C-n, �����<TD>���Υ�˰�ư���ޤ��� +<TR><TD>ESC TAB, C-p, �����<TD>���Υ�˰�ư���ޤ��� +</table> + +<H3>�ϥ��ѡ�������</H3> +<table> +<TR><TD WIDTH=100>RET, C-f, �����<TD>���ߥ������뤬�������ؤ����ʸ����ɤߤ��ߤޤ��� +<TR><TD>d, ESC RET<TD>���ߥ������뤬�������ؤ����ʸ���ե��������¸���ޤ��� +<TR><TD>u<TD>���ߥ������뤬�������ؤ����URL��ɽ�����ޤ��� +<TR><TD>i<TD>���ߥ������뤬������ޤ�������Τ�Τ�URL��ɽ�����ޤ��� +<TR><TD>I<TD>���ߥ������뤬�������б����������ɽ�����ޤ��� +<TR><TD>ESC I<TD>���ߥ������뤬�������ؤ�������ե��������¸���ޤ��� +<TR><TD>:<TD>URL����ʸ������ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>ESC :<TD>Message-ID����ʸ�����news: �Υ�ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ����ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>c<TD>���ߤ�ʸ���URL��ɽ�����ޤ��� +<TR><TD>=<TD>���ߤ�ʸ��˴ؤ�������ɽ�����ޤ��� +<TR><TD>F<TD><FRAMESET>��ޤ�ʸ���ɽ�����Ƥ���Ȥ��ˡ�<FRAME> +�����λؤ�ʣ����ʸ���1�Ĥ�ʸ����Ѵ�����ɽ�����ޤ��� +<TR><TD>M<TD>���߸��Ƥ���ڡ��������֥饦����Ȥä�ɽ�����ޤ��� +2M, 3M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +<TR><TD>ESC M<TD>���ߤΥ��������֥饦����Ȥä�ɽ�����ޤ��� +2ESC M, 3ESC M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +</table> + +<H3>�ե������URL�ط������</H3> +<table> +<TR><TD WIDTH=100>g, U<TD>URL����ꤷ�Ƴ����ޤ��� +<TR><TD>V<TD>��������ե��������ꤷ�Ƴ����ޤ��� +<TR><TD>@<TD>���ޥ�ɤ�¹Ԥ�����̤������ɤ�Ǥ���ɽ�����ޤ��� +<TR><TD>#<TD>���ޥ�ɤ�¹Ԥ�����̤��ɤߤ��ߤʤ���ɽ�����ޤ��� +</table> + +<H3>�Хåե����</H3> +<table> +<TR><TD WIDTH=100>B, C-b, �����<TD>���߸��Ƥ���Хåե���������������ΥХåե���ɽ�����ޤ��� +<TR><TD>\<TD>HTML�Υ�������ɽ�����ޤ��� +<TR><TD>s, C-h<TD>�Хåե�����⡼�ɤ�����ޤ��� +<TR><TD>E<TD>���߸��Ƥ���Хåե�����������ե�����ξ�硤���Υե�����ǥ������Խ����ޤ������ǥ�����λ�����塤���Υե����������ɤ߹��ߤޤ��� +<TR><TD>R, C-r<TD>�Хåե�������ɤ߹��ߤޤ��� +<TR><TD>S, p<TD>�Хåե���ɽ�����Ƥ�ե��������¸���ޤ��� +<TR><TD>ESC s<TD>HTML�Υ�������ե��������¸���ޤ���v �ǥ�������ɽ������ S �� +��¸����ΤȤۤ�Ʊ���Ǥ�����ESC s ����¸�����ե�����ϴ��������ɤ����ꥸ�ʥ�� +�ޤޤǤ���Τ��Ф��ơ�v S ����¸����ȸ���ɽ���˻ȤäƤ�����������ɤ��Ѵ����� +����¸����ޤ��� +<TR><TD>ESC e<TD>����ɽ������Ƥ���Хåե���ɽ������Ƥ�������Τޤ� +���ǥ������Խ����ޤ��� +</table> + +<H3>�Хåե�����⡼��</H3> +"s" �ǥХåե�����⡼�ɤ����ä��Ȥ��Υ������Ǥ��� +<table> +<TR><TD WIDTH=100>k,C-p<TD>��ľ�ΥХåե������ޤ��� +<TR><TD>j,C-n<TD>��IJ��ΥХåե������ޤ��� +<TR><TD>D<TD>�������Ƥ���Хåե��������ޤ��� +<TR><TD>RET<TD>�������Ƥ���Хåե���ɽ�����ޤ��� +</table> + +<H3>�֥å��ޡ������</H3> +<table> +<TR><TD WIDTH=100>v, ESC b<TD>�֥å��ޡ������ɤ߹��ߤޤ��� +<TR><TD>a, ESC a<TD>���߸��Ƥ���ڡ�����֥å��ޡ������ɲä��ޤ��� +</table> + +<H3>����</H3> +<table> +<TR><TD WIDTH=100>/<TD>���ߤΥ���������֤���ե����������˸����ä�����ɽ�������ޤ��� +<TR><TD>?<TD>���ߤΥ���������֤���ե��������Ƭ�˸����ä�����ɽ�������ޤ��� +<TR><TD>n<TD>�������ޤ��� +</table> + +<H3>�ޡ������</H3> +<table> +<TR><TD WIDTH=100>C-SPC<TD>�ޡ��������������ޤ����ޡ�����ȿžɽ������ޤ��� +<TR><TD>ESC p<TD>������Υޡ����˰�ư���ޤ��� +<TR><TD>ESC n<TD>��ĸ�Υޡ����˰�ư���ޤ��� +<TR><TD>"<TD>����ɽ���ǻ��ꤵ�줿ʸ��������ƥޡ������ޤ��� +</table> + +<H3>����¾</H3> +<table> +<TR><TD WIDTH=100>!<TD>�����륳�ޥ�ɤ�¹Ԥ��ޤ��� +<TR><TD>H, ?<TD>�إ�ץե������ɽ�����ޤ��� +<TR><TD>o<TD>���ץ��������ѥͥ��ɽ�����ޤ��� +<TR><TD>C-k<TD>���å���������ɽ�����ޤ��� +<TR><TD>C-c<TD>ʸ����ɤ߹��ߤ����Ǥ��ޤ��� +<TR><TD>C-z<TD>�����ڥ�� +<TR><TD>q<TD>w3m��λ���ޤ������ץ���������ˤ�äơ���λ���뤫�ɤ�����ǧ���ޤ��� +<TR><TD>Q<TD>��ǧ������w3m��λ���ޤ��� +</table> + +<H3>���Խ�</H3> +���̤κDz��Ԥ�ʸ��������Ϥ������ͭ���ʥ������Ǥ��� +<table> +<TR><TD WIDTH=100>C-f<TD>��������˰�ư���ޤ��� +<TR><TD>C-b<TD>��������˰�ư���ޤ��� +<TR><TD>C-h<TD>���������ľ����ʸ���������ޤ��� +<TR><TD>C-d<TD>����������֤�ʸ���������ޤ��� +<TR><TD>C-k<TD>����������֤����������ޤ��� +<TR><TD>C-u<TD>����������֤������������ޤ��� +<TR><TD>C-a<TD>ʸ�������Ƭ�˰�ư���ޤ��� +<TR><TD>C-e<TD>ʸ����κǸ�˰�ư���ޤ��� +<TR><TD>SPC<TD>�ե�����̾���ϻ��ˡ��ե�����̾���䴰���ޤ��� +<TR><TD>RETURN<TD>���Ϥ�λ���ޤ��� +</table> + +<hr> +<a name="Mouse"></a> +<h2>�ޥ������</h2> +�ޥ�����ǽ��ON�ˤ��ƥ���ѥ��뤷�Ƥ���С��ޥ�����Ȥä� +w3m�����뤳�Ȥ��Ǥ��ޤ����ޥ������Ȥ���Τϡ�xterm/kterm/rxvt +��ȤäƤ�����(���ξ��ˤϡ��Ķ��ѿ�TERM�� xterm �� kterm �� +���ꤹ��ɬ�פ�����ޤ�)���ޤ��� GPM ��ư���Ƥ���Ķ���ȤäƤ����� +�Ǥ��� + +<p> +<table border=0> +<tr><td>������å� +<td>���������ޥ�����������ΰ��֤˰�ư���ޤ��� +�⤷��������ȥޥ�����������ΰ��֤�Ʊ���ǡ��������뤬 +��ξ�ˤ��ä��Ȥ��ϡ����Υ�ɤ�ޤ��� +<tr><td>�楯��å� +<td>���ΥХåե������ޤ��� +<tr><td>������å� +<td>��˥塼���ޤ�����˥塼�ι��ܤ�ޥ��������֤��Ȥ��Ǥ��ޤ��� +<tr><td>���ɥ�å� +<td>�ڡ����������뤷�ޤ����ǥե���Ȥ�ư��Ǥϡ� +�ޥ����Υɥ�å��˹�碌��ʸ������������뤷�ޤ��� +���ץ��������ѥͥ������ǡ�����ư���դˤ��뤳�� +���Ǥ��ޤ�(�ޥ����Υɥ�å��˹�碌�ơ�������ɥ������� +���������뤹��)�� +</table> +<p> + + +<hr> +<a name="Key:custom"></a> +<h2>���������</h2> +~/.w3m/keymap �Ҥ���ȡ������γ�ꤢ�Ƥ��Ѥ��뤳�Ȥ��Ǥ��ޤ� +(���Խ��Υ�����������)���㤨�С� +<pre> + + keymap C-o NEXT_PAGE + +</pre> +�ȵ��Ҥ���ȡ�NEXT_PAGE��ǽ(�̾凉�ڡ����� C-v)�˳�ꤢ�Ƥ��� +������)�� C-o �˳�ꤢ�Ƥ뤳�Ȥ��Ǥ��ޤ��� +���Ѳ�ǽ�ʵ�ǽ�ȡ�����̾���ˤĤ��Ƥϡ� +<a href="README.func">README.func</a>�Ȥ��Ƥ��������� +��Ȥ��ơ����ꥸ�ʥ��Lynx���Υ�������ե����� +(<a href="keymap.default">keymap.default</a> +��<a href="keymap.lynx">keymap.lynx</a>)���֤��Ƥ���ޤ��� + +<hr> +<a name="LocalCGI"></a> +<h2>Local CGI</h2> +w3m��Ȥ��С�HTTP�����Фʤ���CGI������ץȤ�ư���뤳�Ȥ��Ǥ��ޤ��� +���ΤȤ���w3m�������ФΤդ�ƥ�����ץȤ�ư�������ν��Ϥ� +�ɤߤ����ɽ������櫓�Ǥ��� +<a href="file:///$LIB/w3mbookmark?mode=panel&bmark=~/.w3m/bookmark.html&url=MANUAL.html&title=w3m+manual">�֥å��ޡ�������Ͽ</a>�� +<a href="file:///$LIB/w3mhelperpanel?mode=panel">�����ӥ塼�����Խ�</a> +�ϡ�local CGI�Υ�����ץȤȤ��Ƽ¸�����Ƥ��ޤ��� +local CGI��Ȥ��С�w3m�����ѤΥե��������ϥ��ե������Ȥ��� +�Ȥ����Ȥ��Ǥ��ޤ��� +<P> +�������ƥ������ͳ�ˤ�ꡤ��ư����CGI������ץȤϡ����Τɤ줫�� +�ǥ��쥯�ȥ�ˤ���ɬ�פ�����ޤ��� +<ul> +<li>w3m�Υإ�ץե�����ʤɤ��֤��Ƥ���ǥ��쥯�ȥ� +(ŵ��Ū�ˤ� /usr/local/lib/w3m)�����Υǥ��쥯�ȥ�ϡ� +$LIB �ǻ��Ȥ��뤳�Ȥ��Ǥ��ޤ��� +<li>/cgi-bin/ �ǥ��쥯�ȥꡥ���Υǥ��쥯�ȥ�ϡ�Ǥ�դξ��� +��ꤢ�Ƥ뤳�Ȥ��Ǥ��ޤ�(���ץ��������ѥͥ�Ρ�/cgi-bin��ɽ����� +�ǥ��쥯�ȥ�פι���)�������ˤϡ�: �Ƕ��ڤä�ʣ���Υǥ��쥯�ȥ�� +���ꤹ�뤳�Ȥ��Ǥ��ޤ�(�㤨�� /usr/local/cgi-bin:/home/aito/cgi-bin �ʤ�)�� +������˥����ȥǥ��쥯�ȥ������뤳�Ȥϡ��������ƥ������ͳ�ˤ�� +������ޤ��� +</ul> +<p> +Local CGI�Ȥ��ƻȤ��륹����ץȤǤϡ�w3m��ȥ����뤹�뤿��ˡ� +�ü�ʥإå� `w3m-control:' ��Ȥ����Ȥ��Ǥ��ޤ������Υإå��ˤϡ� +w3m��Ǥ�դε�ǽ (<a href="README.func">README.func</a>����)��� +���Ȥ��Ǥ��ޤ���ʸ��ɽ�����줿�塤���ε�ǽ���ƤӽФ���ޤ��� +�㤨�С� +<pre> + +Content-Type: text/plain +W3m-control: BACK + +</pre> +�Ȥ����إå�����Ϥ�����硤w3m�϶��Υڡ�����ɽ����������ľ��� +���ΥХåե��������ޤ�������ϡ�CGI��¹Ԥ�����ǡ�����ڡ����� +ɽ���������ʤ�����ͭ���Ǥ����ޤ��� +<pre> + +Content-Type: text/plain +W3m-control: DELETE_PREVBUF + +contents..... +</pre> +�ϡ��������Хåե���ľ���ΥХåե����֤������ޤ��� +<p> +��Ĥ� w3m-control: �إå��ˤϡ���Ĥε�ǽ��������ꤹ�뤳�Ȥ��Ǥ��ޤ��� +��������HTTP�쥹�ݥ����ʣ���� w3m-control: ������뤳�Ȥ��Ǥ��� +�����ǻ��ꤵ�줿��ǽ�Ͻ��֤˼¹Ԥ���ޤ��� +����ˡ�GOTO �ˤϰ�������ꤹ�뤳�Ȥ��Ǥ��ޤ��� +<pre> + +Content-Type: text/plain +W3m-control: GOTO http://www.yahoo.com/ + +</pre> +������ϡ�Location: ��Ȥä��������Ʊ���褦��ư��ޤ��� +<pre> + +Content-Type: text/plain +Location: http://www.yahoo.com/ + +</pre> +��������w3m-control: �إå���w3m��������ץȤ�ľ�ܸƤӤ������������� +ͭ���Ǥ���Ʊ��������ץȤ� HTTP�����з�ͳ�ǸƤӤ�������硤 +w3m-control: �إå���̵�뤵��ޤ��� + +</body> +</html> diff --git a/doc-jp/README b/doc-jp/README new file mode 100644 index 0000000..c0ecd10 --- /dev/null +++ b/doc-jp/README @@ -0,0 +1,111 @@ + w3m: WWW wo Miru Tool version beta-990323 + (C) Copyright by Akinori ITO March 23, 1999 + +1. �Ϥ���� + + w3m �ϡ�World Wide Web ���б������ڡ�����Ǥ��������ޤǥڡ�����Ǥ����� +�ƥ����ȥ١��� WWW �ץ饦���Ȥ��Ƥ�Ȥ����Ȥ��Ǥ��ޤ��� + + w3m �ϡ�fm �Ȥ����ڡ������١����Ȥ��ƺ���ޤ�����fm �ˤĤ��Ƥξܺ� +�ϡ�README.fm ���ɤߤ���������w3m �Υڡ�����Ȥ��Ƥδ���Ū����ħ�ϡ�fm +�ȤۤȤ��Ʊ���Ǥ��� + + w3m �ȼ�����ħ�Ȥ��Ƥϡ����Τ褦�ʤ�Τ�����ޤ��� + + ��WWW �б��ʤΤǡ�HTML ��ʸ����ɤ�Ǥ�����ˤϡ�������Υ��é�ä� + �ꡤ�����뤳�Ȥ��Ǥ��롥 + ��Internet message ɽ���Τ���Υ⡼�ɤ����롥���λ���Content-Type: �� + text/html �ξ��ϡ���ưŪ�� HTML ��ʸ��Ȥ���ɽ�����롥�ޤ������Ϥ� + MIME header �Υǥ����ɤ롥 + �����Ƥ��� plain text ʸ����� URL ɽ�������ä���硤������ʬ������ + �ɤ뤳�Ȥ��Ǥ��롥 + + ���ߤ��������ϰʲ��Τ褦�ʤ�ΤǤ��� + + ������饤�����ɽ�����Ǥ��ʤ�(����ϸ���Ū��̵���Ǥ��礦)�� + ��MIME-body �Υǥ����ɤ��Ǥ��ʤ���7bit �ʳ��� Content-Transfer-encoding + ���б����Ƥ��ʤ��� + ������饤��ޥ˥奢�뤬�ϼ塥��ñ�ʻȤ����ˤĤ��Ƥϡ�MANUAL.html ���ɤ� + ���������� + + ����ư���ǧ����Ƥ��� OS �ϰʲ����̤�Ǥ��� + SunOS4.1.x + HP-UX 9.x, 10.x + Solaris2.5.x + Linux 2.0.30 + FreeBSD 2.2.8, 3.1, 3.2 + NetBSD/macppc, m68k + EWS4800 Rel.12.2 Rev.A + Digital UNIX: v3.2D, v4.0D + IRIX 5.3, IRIX 6.5 + OS/2 with emx + Windows 9x/NT with Cygwin32 b20.1 + MS-DOS with DJGPP and WATT32 packet driver + MacOS X Server + +2. ���ȡ��� + +���ȡ����ˤϡ����Τ褦�ˤ��ޤ��� + + 2.1 configure ��¹Ԥ��롥�����Ĥ����䤵���Τǡ�����������ޤ��� + + 2.2 make ��¹� + + 2.3 make install ��¹� + +�ʾ�ǥ��ȡ���Ͻ����Ǥ��� + +���老�Ȥ��������� + +HP-UX + HP �� C ����ѥ���(gcc�Ǥʤ�)�ǥ���ѥ��뤹���硤 + configure �� + + Input your favorite C-compiler. + (Default: cc) + + �ˡ�cc -Aa -D_HPUX_SOURCE �������Ƥ���������cc + �������ȥ���ѥ��뤬�̤�ޤ���gcc �ʤ����̤� + �̤�ޤ������С������Ť�ξ��� -g ���դ��� + ���Ǥ��������� + +OS/2 + emx ��Ȥ����Ȥ� w3m ��ѥ��뤹�뤳�Ȥ��Ǥ��ޤ��� + �ޤ��ǽ�� + + cd gc + make -f EMX_MAKEFILE + + ��¹Ԥ��� GC �饤�֥���ѥ��뤷�Ƥ����Ƥ��顤w3m + ���Τ�ѥ��뤷�ޤ����ʤ������顼ɽ�����Ѥˤʤ�餷�� + �Τǡ���Υ����Ѥ˥���ѥ��뤷�������ɤ��Ǥ��礦�� +Windows + README.cygwin ��������� +MS-DOS + README.dj��������� + + +3. ��� + +w3m ������ϡ���ƣ��§��°���Ƥ��ޤ��� +(C) Copyright 1994-1999 by Akinori Ito + +ź�ե饤�֥��Τ�����Boehm GC library ������� Hans-J. Boehm, +Alan J. Demers ����� Xerox Corporation, Silicon Graphics ��°�� +�Ƥ��ޤ��� + +4. ���۾�� + +���Υ��եȥ������ˤ�ä����ѼԤޤ����軰�Ԥ����餫���ﳲ������� +��硤��ԤϤ�����Ǥ������餤�ޤ����λ���������¤�ˤ��� +�ơ����Υ��եȥ������ϡ�ï�Ǥ��Ԥ��Ǥ�ʤ����ѡ����ѡ����ۤǤ� +�ޤ��� + +5. ��� + +���ո��������ۤ��ԤޤǤ����������� + + ������ع������ŻҾ��ز� + ��ƣ ��§ + aito@ei5sun.yz.yamagata-u.ac.jp + http://ei5nazha.yz.yamagata-u.ac.jp/ diff --git a/doc-jp/README.SSL b/doc-jp/README.SSL new file mode 100644 index 0000000..f4dd5b6 --- /dev/null +++ b/doc-jp/README.SSL @@ -0,0 +1,46 @@ +SSL ���ݡ��ȤˤĤ��� + + (2000/11/07) �������� + okabek@guitar.ocn.ne.jp + + �� SSLeay/OpenSSL �饤�֥����̤���, SSL �ݡ��Ȥ��Ƥ��ޤ�. + ���餫���ᥤ�ȡ��뤷�Ƥ����Ƥ�������. + + �� configure ������ץȼ¹Ի�, "5 - Monster model" �ޤ��� "6 - Customize" ���� + �ֻ������Ѳ�ǽ�ˤʤ�ޤ�. + �⤷���ޤ�ư���ʤ��Ȥ���, config.h ������å����ƤߤƤ�������. SSL �����Ѥ� + �뤿��ˤ�, config.h ��, USE_SSL �ޥ������������Ƥ���ɬ�פ�����ޤ�. + �����, SSL ǧ�ڥ��ݡ��Ȥ����Ѥ������, USE_SSL_VERIFY �ޥ���������å��� + �ƤߤƤ�������. + ����ѥ���ǥ��顼���Ф����, ��ե饰�� `-lssl -lcrypto', ����ѥ��� + �ե饰�� '-I(SSLeay/OpenSSL �Υإå�������ǥ��쥯�ȥ�)' �����뤫��ǧ���Ƥ� + ������. + + SSL ���ݡ��Ȥ�ͭ���ˤʤäƤ��뤫�ɤ�����, Option Setting Panel �dz�ǧ�Ǥ��� + ��. + + �� SSL �˴ؤ��ưʲ������꤬��ǽ�ˤʤäƤޤ�: + + ssl_verify_server ON/OFF + SSL�Υ�����ǧ�ڤ�Ԥ�(�ǥե���Ȥ�OFF). + ssl_cert_file �ե�����̾ + SSL�Υ��饤�������PEM����������ե�����(�ǥե���Ȥ�<NULL>). + ssl_key_file �ե�����̾ + SSL�Υ��饤�������PEM������̩���ե�����(�ǥե���Ȥ�<NULL>). + ssl_ca_path �ǥ��쥯�ȥ�̾ + SSL��ǧ�ڶɤ�PEM���������Τ���ǥ��쥯�ȥ�ؤΥѥ� + (�ǥե���Ȥ�<NULL>). + ssl_ca_file �ե�����̾ + SSL��ǧ�ڶɤ�PEM���������Υե�����(�ǥե���Ȥ�<NULL>). + ��������SSLEAY_VERSION_NUMBER >= 0x0800�פʴĶ��Ǥʤ���̵�̤ʥ����ɤ��� + ��������ʤΤ�, configure����disable���Ƥ������ۤ����褤�Ǥ��礦. + + �ޤ��ºݤ�ǧ�ڤ�Ԥ����, ssl_ca_path�ޤ���ssl_ca_file��, �����Фθ��� + ��̾���Ƥ���ǧ�ڶɤξ������ (ssl_verify_server��ON/OFF�˴ط�̵��) ���� + ���ʤ���ǧ�ڤ��������ʤ��褦�Ǥ�. + + �� �С������ 0.9.5 �ʹߤ� OpenSSL �饤�֥���, ������������뤿��˴��Ĥ� + �Υ����ɤ����ꤹ��ɬ�פ�����ޤ�. + �ǥե���ȤǤ� /dev/urandom ������Ф�������Ѥ��ޤ���, ̵����� w3m ���� + ���������ޤ�. �⤷, EGD (Entropy Gathering Daemon) �����ѤǤ���Ķ��Ǥ��� + ��Ȥ���������, USE_EGD �ޥ���������å����ƤߤƤ�������. diff --git a/doc-jp/README.cookie b/doc-jp/README.cookie new file mode 100644 index 0000000..0b8d245 --- /dev/null +++ b/doc-jp/README.cookie @@ -0,0 +1,56 @@ +���å������ݡ��ȤˤĤ��� + + (2000/11/07) �������� + okabek@guitar.ocn.ne.jp + + �� version 0 (����: http://www.netscape.com/newsref/std/cookie_spec.html) ��, + version 1 (����: http://www.ics.uci.edu/pub/ietf/http/rfc2109.txt, + http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt) + �Υ��å����ݡ��Ȥ��Ƥ��ޤ�. + + �� configure ������ץȼ¹Ի�, "4 - Cookie model", "5 - Monster model", "6 - + Customize" �Τ����줫�����֤����Ѳ�ǽ�ˤʤ�ޤ�. + �⤷���ޤ�ư���ʤ��Ȥ���, config.h ������å����ƤߤƤ�������. ���å������� + �Ѥ��뤿��ˤ�, config.h �� USE_COOKIE �ޥ������������Ƥ���ɬ�פ������ + ��. + + �� Option Setting Panel (�̾� `o' �����˥Х���ɤ���Ƥ���), �ޤ��ϵ�ư���ץ� + ��� (-cookie, -no-cookie) �ǻ��Ѥ��뤫�ݤ�������Ǥ��ޤ�. + + �� Option Setting Panel �ǥ��å���������դ��ʤ��褦�ˤ������Ǥ��ޤ�. + ���ξ��, �ʸ�Υ����Ф��������Ƥ������ƤΥ��å����ϥꥸ�����Ȥ���ޤ���, + ���˼�����äƤ��륯�å����ˤĤ��ƤϷ�³���ƻ��Ѥ���ޤ�. + + �� C-k �ǥ��å���������ɽ���Ǥ��ޤ�. ���β��̤�, ���å�����˻��Ѥ��뤫�ݤ��� + ����Ǥ��ޤ�. + + �� 2000/10/24 ���Ǥ���, Option Setting Panel �ǥ��å���������դ��� (�ޤ��ϼ� + ���դ��ʤ�) �ɥᥤ�� (�Υꥹ��) ������Ǥ���褦�ˤʤ�ޤ���. �ʲ��Υե��� + �ޥåȤǻ��ꤷ�ޤ�: + + domain-list = domains + | "" + domains = domain + | domain + "," + domains + domain = "." + domain-name ; �ɥᥤ��̾�ȥޥå� + | host-domain-name ; HDN �ȥޥå� + | ".local" ; . ��ޤޤʤ����Ƥ� HDN �˥ޥå� + | "." ; ���Ƥ� HDN �˥ޥå� + + (HDN: host domain name) + + ��Ȥ���, ����Υɥᥤ�� (.xxx.or.jp) �Υ��å����Τߤ�����դ���������, + + ���������������������������������������������������������������������������� + �����å��������� �� + �� �� + �����å�������Ѥ��� (*)ON ( )OFF �� + �����å���������դ��� (*)ON ( )OFF �� + ������Τ��륯�å����Ǥ�����դ��� [discard] �� + �����å���������դ��ʤ��ɥᥤ�� [. ] �� + �����å���������դ���ɥᥤ�� [.xxx.or.jp ] �� + �� [OK] �� + ���������������������������������������������������������������������������� + + �Τ褦�����ꤷ�ޤ�. + diff --git a/doc-jp/README.cygwin b/doc-jp/README.cygwin new file mode 100644 index 0000000..08b01cf --- /dev/null +++ b/doc-jp/README.cygwin @@ -0,0 +1,27 @@ +***Windows �� w3m ��ư�����ˤ�*** + +Windows �� w3m ��ư��������ˤϡ�Windows ��� UNIX�ߴ��Ķ��Ǥ��� Cygwin32 +(��ȯ�Ķ��դ�)��ɬ�פǤ���Cygwin32 �˴ؤ������� http://sourceware.cygnus.com/cygwin/ +����������ޤ��� + +����ѥ��뤹��ˤϡ� + + sh configure + +��¹Ԥ��Ƥ��� make ���ޤ����¹����ˡ� + + TERM=ansi; export TERM + +��¹Ԥ��Ƥ����Ȥ褤�餷���Ǥ��� + +�ʲ��ϡ�Cygwin32 �� w3m ��ư�����������������Ǥ��� +[���Ĥ���(ueda@iias.flab.fujitsu.co.jp)���] + +Known Bugs: +cygwin32 �ǡ���������ե������֥å��ޡ����ˤ��褦�Ȥ���ȡ�URL������ +�� file://c:540390910/path ���뤤�ϡ�file://C//c/path �Τ褦�ˤʤäƤ� +�ޤ������������Ȥ��뤳�Ȥ��Ǥ��ʤ��ʤ롣 +��Ͽ���� file:////c/path �Τ褦��ľ������Ͽ���뤳�ȤDz��Ǥ��롣 + +����: cygwin32 �Ǥϥե�����̾�� //�ɥ饤��̾/�ѥ� ��ɽ����뤬������� +loadGeneralFile() ���ɤ߹������url ��ְ㤨�Ʋ�᤹�뤿�ᡣ diff --git a/doc-jp/README.dict b/doc-jp/README.dict new file mode 100644 index 0000000..0a68bef --- /dev/null +++ b/doc-jp/README.dict @@ -0,0 +1,41 @@ +w3m�ѱ��¼�ŵ������ǽ�ˤĤ��� + +1. �Ϥ���� + +'webster' ���ޥ�ɤʤɤΤ褦�˼�����������Υ��ޥ�ɤ����� +���ˤϡ�w3m ���椫�餽���Ȥ����Ȥ��Ǥ��ޤ������ε�ǽ�ϡ� +��Ӥ�������(rubikitch@ruby-lang.org)�ˤ���ΤǤ��� + +2. ���ȡ��� + +���ε�ǽ��Ȥ�����ˤϡ�����ѥ��륪�ץ������ǽ����� +����ѥ��뤷�ʤ���ɬ�פ�����ޤ���configure ��¹Ԥ��� config.h +���������줿�塤config.h ���Խ����� + +#undef DICT + +�� + +#define DICT + +���ѹ�����w3m ��ѥ��뤷�ʤ����Ƥ��������� +(dict.c �� keybind.c ��ѥ��뤷�ʤ������ɤ��Ϥ��Ǥ�) + +���줫�顤w3mdict �Ȥ������ޥ�ɤ��Ѱդ��ޤ�������ϡ���¸�� +����������ޥ�ɤؤΥ�Ǥ����㤨�С�webster �Ȥ������ޥ�� +������˻Ȥ��������ˤϡ����Τ褦�ˤ��Ƥ��������� + +% cd /usr/local/bin +% ln -s `which webster` w3mdict + +���̤ˡ�ñ�������Ȥ��Ƽ�äơ�ɸ����Ϥ˲�������Ϥ��륳�ޥ�� +�Ǥ���С��ɤ�ʤ�ΤǤ� w3mdict �Ȥ��ƻȤ����Ȥ��Ǥ��ޤ��� + +3. �Ȥ����� + +����2�ĤΥ��ޥ�ɤ��Ȥ���褦�ˤʤ�ޤ��� + +ESC w ñ������Ϥ��������ǰ�����ɽ�����ޤ��� + +ESC W �Хåե���θ��ߥ������뤬����ñ���ǰ�����ɽ�����ޤ��� + diff --git a/doc-jp/README.func b/doc-jp/README.func new file mode 100644 index 0000000..77a1959 --- /dev/null +++ b/doc-jp/README.func @@ -0,0 +1,85 @@ +ABORT ��ǧ������w3m��λ���ޤ� +ADD_BOOKMARK ���߸��Ƥ���ڡ�����֥å��ޡ������ɲä��ޤ� +BACK ������ΥХåե���ɽ�����ޤ� +BEGIN ʸ��Τ����Ф��ιԤ˰�ư���ޤ� +BOOKMARK �֥å��ޡ������ɤ߹��ߤޤ� +CENTER_H ��������Τ�����֤�Ԥ�����˰�ư���ޤ� +CENTER_V ��������Τ���Ԥ���̤�����˰�ư���ޤ� +COOKIE ���å���������ɽ�����ޤ� +DICT_WORD ���Ϥ���ñ��ޥ�ɤ�Ĵ�٤ޤ� +DICT_WORD_AT ����������֤�ñ��ޥ�ɤ�Ĵ�٤ޤ� +DOWN ���̤�1�Բ��˥��������뤷�ޤ� +DOWN_LOAD HTML�Υ�������ե��������¸���ޤ� +EDIT ���ǥ������Խ����ޤ� +EDIT_SCREEN ����ɽ������Ƥ���Хåե��ǥ������Խ����ޤ� +END ʸ��Τ����ФιԤ˰�ư���ޤ� +EXEC_SHELL �����륳�ޥ�ɤ�¹Ԥ��ޤ� +EXIT ��ǧ������w3m��λ���ޤ� +EXTERN �����֥饦����Ȥä�ɽ�����ޤ� +EXTERN_LINK ���ߤΥ��������֥饦����Ȥä�ɽ�����ޤ� +FRAME <FRAME>�����λؤ�ʸ���1�Ĥ�ʸ����Ѵ�����ɽ�����ޤ� +GOTO URL����ꤷ�Ƴ����ޤ� +GOTO_LINE ���̲��ǹ��ֹ�����Ϥ��������ǻ��ꤷ���Ԥ˰�ư���ޤ� +GOTO_LINK ����ؤ����ʸ����ɤߤ��ߤޤ� +HELP �إ�ץե������ɽ�����ޤ� +HISTPRY URL�����ɽ�����ޤ� +INFO ���ߤ�ʸ��˴ؤ�������ɽ�����ޤ� +INTERRUPT ʸ����ɤ߹��ߤ����Ǥ��ޤ� +LEFT �������Τ�1ʸ�����ˤ��餷�ޤ� +LINE_BEGIN ��Ƭ�˰�ư���ޤ� +LINE_END �����˰�ư���ޤ� +LINE_INFO ���ߤ�ʸ��˴ؤ�������ɽ�����ޤ� +LOAD ��������ե��������ꤷ�Ƴ����ޤ� +MAIN_MENU ��˥塼��Ω���夲�ޤ� +MARK �ޡ��������������ޤ� +MARK_MID Message-ID����ʸ�����news:�Υ�ˤ��ޤ� +MARK_URL URL����ʸ������ˤ��ޤ� +MENU ��˥塼��Ω���夲�ޤ� +MOUSE_TOGGLE �ޥ�����ͭ��/̵����ȥ��뤹�� +MOVE_DOWN ��������˰�ư���ޤ� +MOVE_LEFT ��������˰�ư���ޤ� +MOVE_RIGHT ��������˰�ư���ޤ� +MOVE_UP ����������˰�ư���ޤ� +NEXT_LINK ���Υ�˰�ư���ޤ� +NEXT_MARK ��ĸ�Υޡ����˰�ư���ޤ� +NEXT_PAGE ���Υڡ�����ɽ�����ޤ� +NEXT_WORD ����ñ��˰�ư���ޤ� +NOTHING ���⤷�ޤ��� +NULL ���⤷�ޤ��� +OPTIONS ���ץ��������ѥͥ��ɽ�����ޤ� +PEEK ���ߤ�ʸ���URL��ɽ�����ޤ� +PEEK_LINK ����ؤ����URL��ɽ�����ޤ� +PEEK_IMG ���ߥ������뤬������ޤ�������Τ�Τ�URL��ɽ�����ޤ� +PIPE_SHELL ���ޥ�ɤ�¹Ԥ�����̤��ɤߤ��ߤʤ���ɽ�����ޤ� +PREV_LINK ���Υ�˰�ư���ޤ� +PREV_MARK ������Υޡ����˰�ư���ޤ� +PREV_PAGE ���Υڡ�����ɽ�����ޤ� +PREV_WORD ����ñ��˰�ư���ޤ� +PRINT �Хåե���ɽ�����Ƥ�ե��������¸���ޤ� +QUIT w3m��λ���ޤ� +READ_SHELL ���ޥ�ɤ�¹Ԥ�����̤������ɤ�Ǥ���ɽ�����ޤ� +REDRAW �����褷�ޤ� +REG_MARK ����ɽ���ǻ��ꤵ�줿ʸ��������ƥޡ������ޤ� +RELOAD �Хåե�������ɤ߹��ߤޤ� +RIGHT �������Τ�1ʸ�����ˤ��餷�ޤ� +SAVE HTML�Υ�������ե��������¸���ޤ� +SAVE_IMAGE ����ؤ�������ե��������¸���ޤ� +SAVE_LINK ����ؤ����ʸ���ե��������¸���ޤ� +SAVE_SCREEN �Хåե���ɽ�����Ƥ�ե��������¸���ޤ� +SEARCH �ե����������˸����ä�����ɽ�������ޤ� +SEARCH_BACK �ե��������Ƭ�˸����ä�����ɽ�������ޤ� +SEARCH_FORE �ե����������˸����ä�����ɽ�������ޤ� +SEARCH_NEXT �������ޤ� +SEARCH_PREV �������ޤ� +SELECT �Хåե�����⡼�ɤ�����ޤ� +SHELL �����륳�ޥ�ɤ�¹Ԥ��ޤ� +SHIFT_LEFT �������Τˤ��餷�ޤ� +SHIFT_RIGHT �������Τˤ��餷�ޤ� +SOURCE HTML�Υ�������ɽ�����ޤ� +SUSPEND �����ڥ�� +UP ���̤�1�Ծ�˥��������뤷�ޤ� +VIEW HTML�Υ�������ɽ�����ޤ� +VIEW_BOOKMARK �֥å��ޡ������ɤ߹��ߤޤ� +VIEW_IMAGE ����б����������ɽ�����ޤ� +WHEREIS �ե����������˸����ä�����ɽ�������ޤ� +WRAP_TOGGLE �ޤ��֤������⡼�ɤ��ڤ괹���ޤ� diff --git a/doc-jp/README.hp b/doc-jp/README.hp new file mode 100644 index 0000000..99e80bb --- /dev/null +++ b/doc-jp/README.hp @@ -0,0 +1,28 @@ +HP-UX �� w3m ��ѥ��뤹��������� + +PA-RISC 2.0 �� HP-UX 11.x ��ư�����Ƥ����硤�ʲ��Υѥå� +�� gc �饤�֥������ƤƤ��ɬ�פ�����ޤ������Υѥå��ϡ� +Dave Eaton <dwe@arde.com> �ˤ���ΤǤ��� + +HP �� C ����ѥ���(gcc�Ǥʤ�)�ǥ���ѥ��뤹���硤 +configure �� + + Input your favorite C-compiler. + (Default: cc) + +�ˡ�cc -Aa -D_HPUX_SOURCE �������Ƥ���������cc +�������ȥ���ѥ��뤬�̤�ޤ���gcc �ʤ����̤� +�̤�ޤ������С������Ť�ξ��� -g ���դ��� +���Ǥ��������� + +---------------------------------------------------------------------- +--- w3m/gc/gcconfig.h.original Wed May 19 01:38:55 1999 ++++ w3m/gc/gcconfig.h Tue Jun 8 12:38:22 1999 +@@ -125,6 +125,7 @@ + # define mach_type_known + # endif + # if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ ++ || defined(_PA_RISC2_0) \ + || defined(hppa) || defined(__hppa__) + # define HP_PA + # define mach_type_known diff --git a/doc-jp/README.keymap b/doc-jp/README.keymap new file mode 100644 index 0000000..cf656e1 --- /dev/null +++ b/doc-jp/README.keymap @@ -0,0 +1,58 @@ + +w3m �Υ����Х���ɤˤĤ��� + (1999/06/30) ���� ��§ + hsaka@mth.biglobe.ne.jp + + ~/.w3m/keymap �˥����Х���ɤ�����Ǥ��ޤ��� + ������ˡ�ϡ� + + keymap ���� ���ޥ�� [����] + + �Ǥ��� + ��� keymap.default �� keymap.lynx �Ƥ��������� + ����Ǥ��륳�ޥ�ɤ� README.func �Ƥ��������� + + ʣ��ʸ������ʤ륨�������ץ������ϡ� + Escape ʸ�� + Escape [ ʸ��, Escape O ʸ�� + Escape [ ���� ~, EScape [ ���� ���� ~ + �Τ������ǽ�Ǥ��� + + �ü�ʸ���ϡ� + + Ctrl : C-, ^ + Escape : ESC-, M-, \e, ^[ + Space : SPC, ' ' + Tab : TAB, \t, ^i, ^I + Delete : DEL, ^? + BackSpace: \b, ^h, ^H + NewLine : \n, ^j, ^J + Return : \r, ^m, ^M + Bell : \a, ^g, ^G + Up : UP, ^[[A + Down : DOWN, ^[[B + Right : RIGHT, ^[[C + Left : LEFT, ^[[D + ^ : \^ + + �Ȥ���ɽ�����Ȥ��Ǥ��ޤ��� + �ޤ���ü���ˤ�äƤϡ� + + Insert : ^[[2~ + PageUp : ^[[5~ + PageDown: ^[[6~ + F1 : ^[[11~ + F2 : ^[[12~ + F3 : ^[[13~ + F4 : ^[[14~ + F5 : ^[[15~ + F6 : ^[[17~ + F7 : ^[[18~ + F8 : ^[[19~ + F9 : ^[[20~ + F10 : ^[[21~ + Help : ^[[28~ + + �ʤɤ���Ѳ�ǽ���⤷��ޤ��� + (�ƥ����Υ����ɤ� Ctrl-V + ���� �dz�ǧ�Ǥ��ޤ���) + diff --git a/doc-jp/README.kokb b/doc-jp/README.kokb new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc-jp/README.kokb diff --git a/doc-jp/README.mailcap b/doc-jp/README.mailcap new file mode 100644 index 0000000..216e28a --- /dev/null +++ b/doc-jp/README.mailcap @@ -0,0 +1,45 @@ +mailcap ���ݡ��ȤˤĤ��� + + (2000/11/07) �������� + okabek@guitar.ocn.ne.jp + + �� 2000/10/6 ���Ǥ���, mailcap �Υե������ test, nametemplate, needsterminal, + copiousoutput, edit (����: RFC 1524) ��褦�ˤʤ�ޤ���. + �ޤ� 2000/10/26 ���Ǥ����, mailcap �� mime.types �ե�����ξ�꤬ Option + Setting Panel ���ѹ��Ǥ���褦�ˤʤ�ޤ���. + + �� mailcap ��� %s �ϳ������ޥ�ɤ��Ϥ��ѥ�̾, %t �� content-type ���ִ������ + ��. + + �� �⤷ mailcap ����ȥ�� test=command �ե�����ɤ�¸�ߤ����� command �μ� + �Է�̤����Ǥ���Ȥ��Τ�, �������ޥ�ɤ��¹Ԥ���ޤ�. �㤨�� + + image/gif; xv '%s'; test=test "$DISPLAY" + + �Τ褦�˽�, DISPLAY �Ķ��ѿ������åȤ���Ƥ���Ȥ��Τ� xv ���¹Ԥ���� + ��. + + �� �⤷ mailcap ����ȥ�� copiousoutput �ե�����ɤ�¸�ߤ�����, �������ޥ� + �ɤ�ɸ����ϤϥХåե����ɤ߹��ޤ�ޤ�. + ��: + + application/x-troff-man;/usr/bin/nroff -mandoc;copiousoutput + + LESSOPEN ���ޥ�ɤǤǤ������, ����ʬ������֤�����������Ǥ��ޤ�. ���Τ��� + LESSOPEN �����Ѥϥ��ץ�����ˤʤ�ޤ���. + + w3m �γ�ĥ�ե�����ɤȤ���, htmloutput ������ޤ�. copiousoutput ��Ʊ�ͤ˥� + �ޥ�ɤμ¹Է�̤��Хåե����ɤ߹��ޤ�ޤ���, HTML �Ȥ��ƥ��������� + ���ۤʤ�ޤ�. ���ε�ǽ��Ȥ�����, w3m �Ѥ� mailcap �ե�������̤��Ѱդ��� + ��, ¾�Υ֥饦���Ѥ� htmloutput ��ޤޤʤ�����ȥ�������֤��Ƥ������ɤ��� + �⤷��ޤ���. + + �� nametemplate= �ϳ������ޥ�ɤ��Ϥ��ե�����̾�γ�ĥ�Ҥ���ꤷ�ޤ�. + �ƥ�ݥ��ե��������Ȥ�, �̾︵�� URL �γ�ĥ�Ҥ��ե�����̾���դ����� + ����, nametemplate= �ե�����ɤˤ�ä��ѹ���������Ǥ��ޤ�. + ��: + + application/x-dvi;xdvi '%s';test=test -n "$DISPLAY";nametemplate=%s.dvi + + �� needsterminal, edit �ˤĤ��Ƥ� RFC 1524 �Ƥ�������. + diff --git a/doc-jp/README.menu b/doc-jp/README.menu new file mode 100644 index 0000000..6700e65 --- /dev/null +++ b/doc-jp/README.menu @@ -0,0 +1,99 @@ + +w3m �Υ�˥塼�ˤĤ��� + (1999/11/03) ���� ��§ + hsaka@mth.biglobe.ne.jp + +[1] ������� + + ������Ū�ʤ�� + + HELP, INS ���� : ��˥塼��Ω���夲 + HELP, INS ����, C-c, : ��˥塼�ξõ� + RET(C-m, C-j), SPC, ������ : ���� + BS(C-h), DEL(C-?), ������ : ��� + C-n, j, ������ : ���ι��ܤ� + C-p, k, ������ : ��ι��ܤ� + C-a : ��Ƭ�ι��ܤ� + C-e : �Ǹ�ι��ܤ� + C-f, C-v : ���ڡ����ι��ܤ� + C-b, M-v : ���ڡ����ι��ܤ� + C-z : �����ڥ�� + + # INS ���̾� ^[[2~ �Ǥ��� ^[[L(������), ^[[E(PocketBSD) �ˤ� + �Х���ɤ��Ƥ���ޤ��� + + MenuKeymap, MenuEscKeymap, MenuEscBKeymap, MenuEscDKeymap (menu.c) + ����� + + �����̤Υ�˥塼�� + + MenuItem ��¤�� (menu.h) �� char *keys ������(ʣ����ǽ) + �嵭�Υ������˾����ޤ��� + +[2] �ޥ��� + + �ܥ��� : ��˥塼��Ω���夲 + + Ω���夲�� + + �ܥ���/�� (����) : ���� + �� (��,MENU_NOP) : ���⤷�ʤ� + �� (�ȳ�) : ���(��˥塼�ξõ�) + �� ( : ) : ���ڡ����ޤ������ڡ����ι��ܤ� + (Ĺ����˥塼�ξ��) + +[3] ��˥塼�Υ������ޥ��� + + ~/.w3m/menu �˥�˥塼������Ǥ��ޤ��� + ��˥塼�ϡ� + + menu MENU_ID + ���� + : + end + + �Ȥ������ꤷ�ޤ����ƹ��ܤˤϡ� + + func LABEL FUNCTION KEYS [DATA] ���ޥ�ɼ¹� + popup LABEL MENU_ID KEYS ���֥�˥塼Ω���夲 + nop LABEL ���⤷�ʤ�(���ѥ졼���䥿���ȥ�) + + �������ǽ�Ǥ��� + ��� menu.default �� menu.submenu �Ƥ��������� + ����Ǥ��륳�ޥ��(FUNCTION)�� README.func �Ƥ��������� + MENU_ID �Ȥ��� "Main" �ϥᥤ���˥塼�ˡ�"Select" �ϥХåե����� + ��˥塼��ͽ��Ƥ��ޤ��� + KEYS �ϥХ���ɤ��륭����ʣ�������ǽ�Ǥ��� + DATA �����ꤵ��Ƥ���Х��ޥ��(FUNCTION)�ΰ����Ȥ��ƻȤ��ޤ��� + +[4] ��ȯ�ˤĤ��� + + ��˥塼�롼�������� mainMenu(), optionMenu() ���ʬ����Ȼפ��ޤ��� + ���Υ롼����Ȱʲ��� MenuItem ��¤�Τ��������С��ۤȤ�ɤΥ�˥塼�ϡ� + ����Ǥ���Ȼפ��ޤ��� + + MenuItem ��¤�� (menu.h) + + struct { + int type; /* ������ */ + char *label; /* ��٥� */ + int *variable; /* VALUE_MENU �ξ������ꤹ���ѿ� */ + int value; /* VALUE_MENU �ξ������ꤹ���� */ + void (*func)(); /* ���줿���˼¹Ԥ���ؿ� */ + struct _Menu *popup; /* ���֥�˥塼 */ + char *keys; /* �Х���ɤ��륭��(ʣ����ǽ) */ + } MenuItem; + + ������ type �ϰʲ��Τ�Τ����ѤǤ��ޤ��� + + MENU_NOP (1) : �ʤˤ⤷�ʤ��������Ǥ��ʤ��� + (�����ȥ�䥻�ѥ졼����) + MENU_FUNC (2) : �ؿ���¹� + MENU_VALUE (4) : �ѿ�(*variable)����(value)������ + MENU_POPUP (8) : ���֥�˥塼��Ω���夲�� + + MENU_FUNC �� MENU_VALUE �� (MENU_FUNC | MENU_VALUE) �� + ���ꤹ�뤳�Ȥ�ξ����ư��ޤ���(�ѿ����꤬��Ǥ�) + + ������ϡ�MainMenuItem (menu.c) �� new_option_menu() �Ƥ��������� + diff --git a/doc-jp/STORY.html b/doc-jp/STORY.html new file mode 100644 index 0000000..b18aa32 --- /dev/null +++ b/doc-jp/STORY.html @@ -0,0 +1,190 @@ +<html> +<head> +<title>w3m�γ�ȯ�ˤĤ���</title> +</head> +<body> +<h1>w3m�γ�ȯ�ˤĤ���</h1> +<div align=right> +1999/2/18<br> +1999/3/8����<br> +��ƣ ��§<br> +aito@ei5sun.yz.yamagata-u.ac.jp +</div> +<h2>�Ϥ����</h2> +w3m�ϡ�WWW���б������ڡ�����/�֥饦���ǡ��ƥ����ȥ١�����ư���� +w3m�˺Ǥ�ᤤ���ץꥱ�������Ȥ��ơ�ͭ̾�ʥƥ����ȥ١����֥饦�� +<a href="http://www.lynx.browser.org/">Lynx</a>�����롥��������w3m�ˤ� +Lynx�ˤʤ������Ĥ�����ħ�����롥�㤨�С� +<UL> +<LI>table��������Ǥ��롥 +<LI>frame��������Ǥ���(frame��table���Ѵ�����ɽ���������)�� +<LI>ɸ�����Ϥ��ɤ��ɽ�����뤳�Ȥ��Ǥ��롥 +(�Ƕ��Lynx �Ǥϡ���������ˤ���ɸ�����Ϥ���ʸ����ɤळ�Ȥ��Ǥ��� +�����Ǥ��� +<pre> + lynx /dev/fd/0 < file +</pre> +����Linux�ǤϳΤ���ư���褦�Ǥ���) +<LI>�ڤ��ƾ�������strip�������w3m�ΥХ��ʥꥵ�����ϡ�Sparc�ξ��� +260KByte��Ǥ���(version beta-990217����)�� +���ʤߤ�Lynx�ΥХ��ʥ�� 1.8MB�ʾ夢�롥 +</UL> +�ʤɤ����������Lynx ��ͥ�줿�֥饦���ǡ�w3m�ˤʤ�¿���ε�ǽ�� +���äƤ��롥Lynx�ˤ��ä�w3m�ˤʤ���ǽ�ϡ��㤨�� +<UL> +<LI>Cookie�б��� +<LI>˭�٤ʥ��ץ�������ꡥ +<LI>¿����б��� +</UL> +�ʤɤʤɡ�Lynx �ˤ�˭�٤ʥɥ�����Ȥ⤢�ꡤ����w3m�ˤϤۤȤ�ɤޤȤ�� +�ɥ�����Ȥ��ʤ����ɥ�����ȤϺ���β������ +<P> +�Ȥ����櫓�ǡ�w3m�ϴ�¸�Υ֥饦��(Netscape�Ϥ������Lynx��)������ +�����ΤǤ�<strong>�ʤ�</strong>������Ǥ�w3m�ϲ��Τ���ˤ���Τ��� +����ϡ�����Ū�ˡ֤���äȡ� web ��Ȥ������������������³���줿�Ķ��ǡ� +�֤���ä�web�˹Ԥ������פȤ���Netscape��Ω��������Τϥ��饤�餹�롥 +Lynx��Ω��������Τˤ���äȴ֤����롥 +��������w3m�ϰ�֤�Ω�������ꡤ�ޥ���ˤۤȤ����ô���ʤ��� +�����Ǿ���ơ���äȾܺ٤˸������Ȥ��ˡ��Ϥ����¾�Υ֥饦����Ȥ� +�Τ�����äȤ��ξ�硤�ۤȤ�ɤ�w3m�����ǽ�ʬ�ʤΤ����� + +<h2>w3m������</h2> +<P> +w3m �����Ȥϡ�<a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/aito-soft.html#fm">fm</a> +�Ȥ����ڡ�����(more��less�ο���)���ä���fm���줿�Τ�1991ǯ���� +(��Ͽ���Ƥ��ʤ��ä��Τ����Τ����դϤ狼��ʤ�)�ǡ������ޤ�WWW�� +����Ū�ǤϤʤ��ä�(¸�ߤ��ʤ��ä�����)�����������֥֥饦���פȤ����С�less�ʤɤ� +�ե������ġ���Τ��Ȥ�ؤ��Ƥ����� +<P> +fm �ϡ������䤬�Ƥ��� +�����ѤΥץ�������ǥХå����뤿��˽���Τ����ץ������ξ��� +��ȥ졼�����뤿�ᡤ�ץ��������������֤�䡹�ȥե�����˥���פ��� +����ʤ���ǥХå��Ƥ�������������Ǥ��������֤�1�Ԥ˥ץ��� +���Ƥ������ᡤ���Υե������1�Ԥ���ɴʸ�����ä��������more��less�� +����ȡ��Ԥ��ޤ��֤���뤿�ᡤ�����������狼��ʤ��ʤäƤ��ޤ��Τ��ä��� +�����ǻ�ϡ��Ԥ��ޤ��֤��ʤ��ڡ�����Ǥ���fm�����ʪ��Ū��1�Ԥ� +���̤ξ�Ǥ�1�Ԥǡ����̤���Ϥ߽Ф�����ʬ��ˤϡ��������Τ餹 +�Ȥ����߷פˤ������������80x24�β��̤�ȤäƤ����Τǡ�fm �ϥǥХå� +�ˤȤƤ���Ω�ä��� +<P> +���Τ��������WWW��¸�ߤ��ΤäƻȤ��Ϥ���������ȤäƤ����֥饦���ϡ� +XMosaic �� Chimera ���ä����ä� Chimera �Ϸڤ��Τǰ��Ѥ��Ƥ����� +��̣�����ä��Τ� HTML �� HTTP ���ٶ��Ƥߤ������Ƴ���ñ�ʤΤǡ� +����ʤ鼫ʬ�Ǥ�֥饦������ΤǤϤʤ����Ȼפä���������HTTP�� +GOPHER�ץ��ȥ�����Ӥ����������٤ǡ����˴�ñ�ʤ�Τ��ä����ޤ��� +HTML �� 2.0 �ǡ��Ԥ��ޤ��֤��Ȳվ���ۤȤ�����Ƥ��ä��� +�����ǡ�fm �ˤ���äȼ������ơ�WWW�֥饦�����äƤߤ������줬w3m���ä��� +���ʤߤˡ�w3m �� WWW-wo-Miru (���ܸ��)��ά�ǡ�fm (File-wo-Miru)�� +��ä����ǽ�� w3m ����Τϡ�1995ǯ��Ƭ���ä��Ȼפ��� + +<h2>w3m������Ⱥ���</h2> +<p> +������衤���äȻ�� w3m ��֥ڡ�����פȤ��ƻȤäƤ������ե������ +�Żҥ�롤�ޥ˥奢��ʤɤ��ɤ�Ȥ��ˡ�less������ˤ��Ƥ����Τ��� +w3m��web�뤳�Ȥ�������ä��������θ� w3m ������˸����ʤ��ڡ����� +¿���ʤä�(����¿����table��ȤäƤ���)���Ȥ⤢�äơ�web�֥饦���� +���ƤϤۤȤ�ɻȤ�ʤ��ʤäƤ��������� table �Υ������Ƥ +�������Ȥ����ä��������Τ����äƤ��ä��� +<P> +�⤦���� w3m �˼������뵤�ˤʤä��Τϡ�1998ǯ�Τ��Ȥ���ư����2�Ĥ��ä��� +������������ϵҰ�������Ȥ��ƥܥ��ȥ���ؤ��ںߤ��Ƥ��ꡤ¿�����֤�;͵�����ä� +���Ȥ���ġ��⤦��Ĥϡ���������� HTML �ǽƤ��ơ���̤�ɤ����Ƥ�ɽ�� +�������ʤä������������ޤǤ�ɽ�� <pre>..</pre>�ǽƤ����Τ����� +plain text��ɽ����Τ��鷺��路���ƻ����ʤ��ä����Ȥ��Ȥ������Ǥ��ʤ��ʤä� +<table>������Ȥä�������������Ⱥ��٤� Netscape ��Ȥ�ʤ������郎 +�����ʤ��ʤäƤ��ޤä��������ǡ�w3m �� table �� +��������Ǥ���褦�ˤ��褦�Ȼ�ߤ��� +<P> +��Ȥ��Ƥϡ�����ۤ�ʣ���Ǥʤ�ɽ�뤳�Ȥ��Ǥ���н�ʬ���ä����Ȥ������� +Ⱦü��table���б�������̡����̤Υ쥤�����Ȥ�table��ȤäƤ���ڡ����� +ɽ���������㤰����ˤʤäƤ��ޤä�����ɡ���ɽ�������ơס֤���¾�Υڡ��� +�⤽�������˸�����פ褦�ˤ��뤿��ˤϡ�table��ɽ���������˶�ʤ���� +�ʤ�ʤ��Τ��ä������ƻ���� +<P> +��ɡ��빽���֤������ä��������Ȥ� +���Ѥˤʤ��Τ��Ǥ����Ȼפ���table �μ����˵���褯���ơ����� form ����� +����������ǡ�w3m�Ϥܼۤ��Ѥˤʤ�֥饦���Ȥ������ޤ��Ѥ�ä��Τ��� + +<h2>w3m�Ǥ�table�Υ�������르�ꥺ��</h2> + +HTML��table�Υ�����Ϸ빽����LaTeX �� tabular �Τ褦�ˡ� +��ɽ�γ����������ꤹ�뤫������ʤ����ɬ�פʺ����������� +�Ȥ����Τʤ��äϴ�ñ�ʤΤ�����HTML��table�ϡֲ��̤�Ŭ���˼��ޤ�褦�ˡ� +����������ꤷ�ơ�ɽ�����Ƥ�Ŭ�����ޤ꤫�����ʤ���Фʤ�ʤ��� +���η����������ˤ���ȡ�����ɽ�����Ť餯�ʤäƤ��ޤ��� +�ޤ���table������ҤˤǤ���Τǡ����줬�ä���ؤ�䤳�������Ƥ��롥 +�����ǡ�w3m�Ǥϼ��Τ褦�ʥ��르�ꥺ���������ꤷ�Ƥ��롥 +<OL> +<LI>�ޤ�����������Ƥκ������ȺǾ�������롥�������Ȥ����Τϡ� +�⤷������Ǥ��������줿�Ȥ����顤���粿��ˤʤ뤫�Ȥ������ +��������Ū�ˤϡ�<BR>��<P>�Ƕ��ڤ�줿�����Ĺ���ˤʤ롥 +�Ǿ����ϡ�����������������������Ƥ��ͤ���ʤ��Ȥ����³����� +�Ǥ��롥ɽ�����Ƥ����ܸ�����ξ��ˤϺǾ����Ͼ��2�Ǥ��ꡤ +internationalization �Ȥ���ñ�줬�ޤޤ�Ƥ���кǾ�����20 +�Ǥ��롥�ޤ���ɽ�����<pre>..</pre>�����ä���硤 +���ΰ�Ԥ�Ĺ���κ����ͤ��Ǿ����ˤʤ롥 +<LI>�⤷��WIDTH°��������������ꤷ�Ƥ���С���������ͤǸ��� +���롥�����������������Ǿ������⾮������С��������Ǿ����Ǹ��ꤹ�롥 +<LI>��κ�����(�ޤ��ϸ�����)���פ��ơ����̤�����������ɤ�����Ĵ�٤롥 +�⤷��פ����̤˼��ޤ�ʤ顤�����ͤ��������Ȥ��ƻȤ��� +<LI>�⤷��פ����̤˼��ޤ�ʤ���С����Τ褦�ˤ���������ꤹ�롥 +<OL> +<LI>���̤������顤�������ꤵ�줿������ι�פ����������� W �Ȥ��롥 +<LI>�������ꤵ��Ƥ��ʤ�����Ф��ơ�����κ��������п������㤷�� W ����ʬ���롥 +<LI>�⤷��ʬ���줿�����Ǿ������⾮������С������������Ǿ����Ǹ��ꤷ�� +������ʬ����ľ���� +</OL> +</OL> +������ʬ����������п������㤵���Ƥ��뤬������Ǥ����Τ��ɤ�����Ƥ���פ��롥 +�����������������Τ�Τ����㤵������Ỵ�ʤ��Ȥˤʤ롥table ����̥쥤������ +�˻ȤäƤ�����硤�������Ĺ��ʸ�Ϥ�����ȡ��������̤����ΤۤȤ�ɤ�Ȥä� +���ޤ���������п�����ʤ��� n �躬�Ǥ⤤�����⤷��ʤ��� +<P> +�嵭�Υ��르�ꥺ��Ǥϡ����̤��������ΤǤ��뤳�Ȥ�����ˤʤäƤ��롥�Ȥ������� +����ǤϺ����礬���롥�ɤ�������礫�Ȥ����ȡ�ɽ������ҤˤʤäƤ�������� +��¦��ɽ���������狼��ʤ�����¦��ɽ��������Ǥ��ʤ�������¦��ɽ�� +��������Ƥߤʤ��ȳ�¦��ɽ����������Ǥ��ʤ��Ȥ���̷��˴٤롥WIDTH°�� +�����ꤷ�Ƥ��������Ϥʤ��Τ����������Ǥʤ����ˤϡ���� +����¦��ɽ�����ϡ���¦��ɽ������0.8�ܡפǷ���Ǥ����Ƥ��ޤ����Ȥˤ����� +�ۤȤ�ɤξ��Ϥ��������ʤ���������ɽ�����ɽ������Ҥˤ���2���¤٤�ȡ� +��¦��ɽ��ɬ�����̤�Ϥߤ����Ƥ��ޤ��褦�ˤʤä����⤷��̩�ˤ������̤˼��� +�褦�Ȥ���ȡ���ö������������Τ�����Ĵ�٤����ȡ��������ꤷ�ʤ����� +�⤦���٥��������Ȥ����������«����ޤǷ����֤��ʤ���Фʤ�ʤ��� +Netscape�ϡ�¿ʬ������äƤ���Τ������� + +<h2>���Ѥ����饤�֥��</h2> + +w3m �ϡ� +<a href="http://reality.sgi.com/boehm/gc.html">Boehm GC</a> +�Ȥ����饤�֥������Ѥ��Ƥ��롥����ϻ䤬����ΤǤϤʤ����� +����ѥ�������ص���ͤ������ۥѥå������˴ޤ�Ƥ��롥 +�ʤ���libwww �ϻȤäƤ��ʤ��� +<P> +Boehm GC�ϡ�C����Ȥ��륬�١������쥯������table ���������������ˤ���� +�Ȥ��Ϥ���Τ��������˲�Ŭ���ä���GC�ʤ��Ǥϡ�w3m��table��form����� +���뺬������ˤ��ä����ɤ������路����Boehm GC�����ѤˤĤ��Ƥϡ��� +<a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/gc/gc.html"> +Boehm GC��Ȥ���</a>�פȤ���ʸ�Ϥ���Τǡ�����⸫�Ƥ�����������ɤ� +�Ȼפ��� +<P> +beta-990304������ΥС������Ǥϡ� +<a href="http://home.cern.ch/~orel/libftp/libftp/libftp.html">LIBFTP</a>�� +�����饤�֥���ȤäƤ����� +libftp ��Ȥä���ͳ�ϡ�FTP�ץ��ȥ��뤬 HTTP ����٤����ݤ��ä�������� +���������饤�������꤬���ꤽ�����Ȥ������ȤʤΤǡ�Ʊ���δؿ�(�Υ��֥��å�) +�����ǽƤ��ޤä��� +<P> +���ʤߤˡ�w3m��UNIX������ɽ���饤�֥��� curses �饤�֥���ȤäƤ��ʤ��� +�ɤ���⼫���δؿ������������������Ѱդ�����ͳ�ϡ�fm��������� +���ܸ���̤�ޤȤ�ǥե������ɽ����curses�Υ饤�֥�꤬�ʤ��ä�������� +���ߤǤϤɤ����¸�ߤ��뤷��¾�Υ饤�֥���Ȥä�����®�����ʤΤ����� +���ݤʤΤǸ��ߤޤǤ��μ�����������äƤ��롥 + +<h2>�����ͽ��</h2> + +...�ʤ���w3m�Ϸڲ��������ʤΤǡ����ޤ굡ǽ�����ܤ��Ƥ��ޤ���w3m�ȼ��� +�ɤ��������뤫������ȤϤ��äƤ⡤�ޤ��Х���¿���Τǡ�������fix�� +���Ƥ��������ȻפäƤ��롥 + +</body> +</html> diff --git a/doc-jp/keymap.default b/doc-jp/keymap.default new file mode 100644 index 0000000..38279ce --- /dev/null +++ b/doc-jp/keymap.default @@ -0,0 +1,115 @@ +# A sample of ~/.w3m/keymap (default) +# +# Ctrl : C-, ^ +# Escape: ESC-, M-, ^[ +# Space : SPC, ' ' +# Tab : TAB, ^i, ^I +# Delete: DEL, ^? +# Up : UP, ^[[A +# Down : DOWN, ^[[B +# Right : RIGHT, ^[[C +# Left : LEFT, ^[[D + +keymap C-@ MARK +keymap C-a LINE_BEGIN +keymap C-b MOVE_LEFT +keymap C-e LINE_END +keymap C-f MOVE_RIGHT +keymap C-g LINE_INFO +keymap C-h HISTORY +keymap TAB NEXT_LINK +keymap C-j GOTO_LINK +keymap C-k COOKIE +keymap C-l REDRAW +keymap C-m GOTO_LINK +keymap C-n MOVE_DOWN +keymap C-p MOVE_UP +keymap C-r SEARCH_BACK +keymap C-s SEARCH +keymap C-v NEXT_PAGE +keymap C-z SUSPEND + +keymap SPC NEXT_PAGE +keymap ! SHELL +keymap \" REG_MARK +keymap # PIPE_SHELL +keymap $ LINE_END +keymap , LEFT +keymap . RIGHT +keymap / SEARCH +keymap : MARK_URL +keymap < SHIFT_LEFT +keymap = INFO +keymap > SHIFT_RIGHT +keymap ? SEARCH_BACK +keymap @ READ_SHELL +keymap B BACK +keymap E EDIT +keymap F FRAME +keymap G END +keymap H HELP +keymap I VIEW_IMAGE +keymap J UP +keymap K DOWN +keymap M EXTERN +keymap Q EXIT +keymap R RELOAD +keymap S SAVE_SCREEN +keymap U GOTO +keymap V LOAD +keymap W PREV_WORD +keymap Z CENTER_H +keymap \^ LINE_BEGIN +keymap a SAVE_LINK +keymap b PREV_PAGE +keymap c PEEK +keymap g BEGIN +keymap h MOVE_LEFT +keymap i PEEK_IMG +keymap j MOVE_DOWN +keymap k MOVE_UP +keymap l MOVE_RIGHT +keymap n SEARCH_NEXT +keymap o OPTIONS +keymap q QUIT +keymap s SELECT +keymap u PEEK_LINK +keymap v VIEW +keymap w NEXT_WORD +keymap z CENTER_V + +keymap M-TAB PREV_LINK +keymap M-C-j SAVE_LINK +keymap M-C-m SAVE_LINK + +keymap M-: MARK_MID +keymap M-< BEGIN +keymap M-> END +keymap M-I SAVE_IMAGE +keymap M-M EXTERN_LINK +keymap M-W DICT_WORD_AT +keymap M-a ADD_BOOKMARK +keymap M-b BOOKMARK +keymap M-e EDIT_SCREEN +keymap M-g GOTO_LINE +keymap M-n NEXT_MARK +keymap M-p PREV_MARK +keymap M-s SAVE +keymap M-v PREV_PAGE +keymap M-w DICT_WORD + +keymap UP MOVE_UP +keymap DOWN MOVE_DOWN +keymap RIGHT MOVE_RIGHT +keymap LEFT MOVE_LEFT + +keymap M-[E MENU +keymap M-[L MENU + +keymap M-[1~ BEGIN +keymap M-[2~ MENU +keymap M-[4~ END +keymap M-[5~ PREV_PAGE +keymap M-[6~ NEXT_PAGE +keymap M-[28~ MENU + diff --git a/doc-jp/keymap.lynx b/doc-jp/keymap.lynx new file mode 100644 index 0000000..6c14a30 --- /dev/null +++ b/doc-jp/keymap.lynx @@ -0,0 +1,109 @@ +# A sample of ~/.w3m/keymap (lynx-like) +# +# Ctrl : C-, ^ +# Escape: ESC-, M-, ^[ +# Space : SPC, ' ' +# Tab : TAB, ^i, ^I +# Delete: DEL, ^? +# Up : UP, ^[[A +# Down : DOWN, ^[[B +# Right : RIGHT, ^[[C +# Left : LEFT, ^[[D + +keymap C-@ MARK +keymap C-a BEGIN +keymap C-b PREV_PAGE +keymap C-e END +keymap C-f NEXT_PAGE +keymap C-h HISTORY +keymap TAB NEXT_LINK +keymap C-j GOTO_LINK +keymap C-k COOKIE +keymap C-l REDRAW +keymap C-m GOTO_LINK +keymap C-n NEXT_LINK +keymap C-p PREV_LINK +keymap C-r RELOAD +keymap C-s SEARCH +keymap C-v NEXT_PAGE +keymap C-w REDRAW +keymap C-z SUSPEND + +keymap SPC NEXT_PAGE +keymap ! SHELL +keymap \" REG_MARK +keymap # PIPE_SHELL +keymap $ LINE_END +keymap + NEXT_PAGE +keymap - PREV_PAGE +keymap / SEARCH +keymap : MARK_URL +keymap < SHIFT_LEFT +keymap = INFO +keymap > SHIFT_RIGHT +keymap ? HELP +keymap @ READ_SHELL +keymap B BACK +keymap E EDIT +keymap F FRAME +keymap G GOTO_LINE +keymap H HELP +keymap I VIEW_IMAGE +keymap J UP +keymap K DOWN +keymap N NEXT_MARK +keymap P PREV_MARK +keymap Q EXIT +keymap R RELOAD +keymap S SAVE_SCREEN +keymap U GOTO +keymap V LOAD +keymap Z CENTER_H +keymap \\ SOURCE +keymap \^ LINE_BEGIN +keymap a ADD_BOOKMARK +keymap b PREV_PAGE +keymap c PEEK +keymap d SAVE +keymap g GOTO +keymap h MOVE_LEFT +keymap i PEEK_IMG +keymap j MOVE_DOWN +keymap k MOVE_UP +keymap l MOVE_RIGHT +keymap n SEARCH_NEXT +keymap o OPTIONS +keymap p SAVE_SCREEN +keymap q QUIT +keymap s SELECT +keymap u PEEK_LINK +keymap v BOOKMARK +keymap z CENTER_V + +keymap M-TAB PREV_LINK +keymap M-C-j SAVE_LINK +keymap M-C-m SAVE_LINK + +keymap M-: MARK_MID +keymap M-I SAVE_IMAGE +keymap M-a ADD_BOOKMARK +keymap M-b BOOKMARK +keymap M-e EDIT_SCREEN +keymap M-s SAVE +keymap M-v PREV_PAGE + +keymap UP PREV_LINK +keymap DOWN NEXT_LINK +keymap RIGHT GOTO_LINK +keymap LEFT BACK + +keymap M-[E MENU +keymap M-[L MENU + +keymap M-[1~ BEGIN +keymap M-[2~ MENU +keymap M-[4~ END +keymap M-[5~ PREV_PAGE +keymap M-[6~ NEXT_PAGE +keymap M-[28~ MENU + diff --git a/doc-jp/menu.default b/doc-jp/menu.default new file mode 100644 index 0000000..b17dad2 --- /dev/null +++ b/doc-jp/menu.default @@ -0,0 +1,33 @@ +# A sample of ~/.w3m/menu (default) +# +# menu MENU_ID +# func LABEL FUNCTION KEYS +# popup LABEL MENU_ID KEYS +# nop LABEL +# end +# +# MENU_ID +# Main: �ᥤ���˥塼 +# Select: �Хåե������˥塼 + +menu Main + func "��� (b)" BACK "b" + func "�Хåե����� (s)" SELECT "s" + func "��������ɽ�� (v)" VIEW "vV" + func "���������Խ� (e)" EDIT "eE" + func "����������¸ (S)" SAVE "S" + func "���ɤ߹��� (r)" RELOAD "rR" + nop "����������������" + func "���ɽ�� (a)" GOTO_LINK "a" + func "�����¸ (A)" SAVE_LINK "A" + func "������ɽ�� (i)" VIEW_IMAGE "i" + func "��������¸ (I)" SAVE_IMAGE "I" + func "�ե졼��ɽ�� (f)" FRAME "fF" + nop "����������������" + func "�֥å��ޡ��� (B)" BOOKMARK "B" + func "�إ�� (h)" HELP "hH" + func "���ץ���� (o)" OPTIONS "oO" + nop "����������������" + func "��λ (q)" QUIT "qQ" +end + diff --git a/doc-jp/menu.submenu b/doc-jp/menu.submenu new file mode 100644 index 0000000..062fac1 --- /dev/null +++ b/doc-jp/menu.submenu @@ -0,0 +1,44 @@ +# A sample of ~/.w3m/menu (submenu type) +# +# menu MENU_ID +# func LABEL FUNCTION KEYS +# popup LABEL MENU_ID KEYS +# nop LABEL +# end +# +# MENU_ID +# Main: �ᥤ���˥塼 +# Select: �Хåե������˥塼 + +menu Main + func "��� (b)" BACK "b" + popup "�Хåե����>(f)" Buffer "fF" + popup "������ >(l)" Link "lL" + nop "����������������" + popup "�֥å��ޡ���>(B)" Bookmark "B" + func "�إ�� (h)" HELP "hH" + func "���ץ���� (o)" OPTIONS "oO" + nop "����������������" + func "��λ (q)" QUIT "qQ" +end + +menu Buffer + popup "�Хåե����� (s)" Select "s" + func "��������ɽ�� (v)" VIEW "vV" + func "���������Խ� (e)" EDIT "eE" + func "����������¸ (S)" SAVE "S" + func "���ɤ߹��� (r)" RELOAD "rR" +end + +menu Link + func "���ɽ�� (a)" GOTO_LINK "a" + func "�����¸ (A)" SAVE_LINK "A" + func "������ɽ�� (i)" VIEW_IMAGE "i" + func "��������¸ (I)" SAVE_IMAGE "I" + func "�ե졼��ɽ�� (f)" FRAME "fF" +end + +menu Bookmark + func "�֥å��ޡ������ɤ߹��� (b)" BOOKMARK "bB" + func "�֥å��ޡ������ɲ� (a)" ADD_BOOKMARK "aA" +end diff --git a/doc-jp/w3m.1 b/doc-jp/w3m.1 new file mode 100644 index 0000000..b90f555 --- /dev/null +++ b/doc-jp/w3m.1 @@ -0,0 +1,443 @@ +.\" +.TH W3M 1 "Jun 6 2000" "UNIX" +.SH NAME +.B w3m +\- text base pager/WWW browser +.SH SYNOPSYS +.B w3m +[options] [file | URL] +.SH DESCRIPTION +.SS �Ϥ���� +.B w3m +�ϡ��ƥ����ȥ١����Υڡ�����/WWW�֥饦���Ǥ��������Ȥ��ȡ� +.I kterm\fR(1) +�ʤɤΥ���饯��ü����ǡ���������ե�������ꡤWWW�����Ƥ��ꤹ�뤳�� +���Ǥ��ޤ��� +.PP +�����˥ե�����̾����ꤹ��Ф��Υե������ɽ������URL����ꤹ��Ф������Ƥ�ɽ +�����ޤ���������ꤷ�ʤ���С�ɸ�����Ϥ����Ƥ�ɽ�����ޤ�����������ɸ�����Ϥ� +.I tty\fR(4) +�Ǥ�����ˤϡ����⤻���˽�λ���ޤ��� +.PP +���ץ����ϼ����̤�Ǥ��� +.TP +.BI + �ֹ� +��ư�塤����ι��ֹ�˰�ư���롥 +.TP +.BI \-t\ �� +���֤�������ꤹ�롥�ǥե���Ȥ� 8�� +.TP +.B \-r +text/plain��ʸ���ɽ�������硤�Ť��Ǥ��ˤ�붯Ĵʸ����ɽ�����ʤ��� +���Υ��ץ������դ��ʤ���硤 +``A^H_'' +��A�Υ�������饤��Ȥ���ɽ�����졤 +``A^HA'' +��A�Υܡ���ɤȤ���ɽ������롥 +.TP +.BI \-l\ �Կ� +ɸ�����Ϥ����Ƥ�ɽ������Ȥ�����¸��������Կ�����ꤹ�롥 +�ǥե���Ȥ�10000�� +.TP +.B \-s +Shift_JIS�����ɤ�ɽ�����롥 +.TP +.B \-e +EUC�����ɤ�ɽ�����롥 +.TP +.B \-j +JIS (ISO-2022-JP)�����ɤ�ɽ�����롥 +.TP +.BI \-T\ ������ +ɽ������ʸ��Υ����פ���ꤹ�롥���λ��꤬�ʤ���硤�ե�����̾�γ�ĥ�Ҥˤ�ä� +��ưȽ�̤���롥Ƚ�̤Ǥ��ʤ�����text/plain�Ȥߤʤ���롥 +.PP +.RS +.B �㡧 +.TP +cat hoge*.html | w3m -T text/html +ɸ�����Ϥ���HTML�ե�������ɤ��ɽ������ +.TP +w3m -T text/plain hoge*.html +HTML�ե�����Υ�������ɽ������ +.RE +.TP +.B \-m +Internet message�⡼�ɤ�ɽ�����롥Internet message�⡼�ɤξ�硤 +�إå������Ƥơ�Content-Type: ������Ф���ͤˤ��롥�Żҥ��� +�ͥåȥ˥塼���ε������ɤ�Ȥ��������� +.TP +.B \-v +visual startup�⡼�ɡ� +���ޥ�ɥ饤������� URL ��ե��������ꤷ�Ƥ��ʤ��Ƥ� +������̤�ɽ�������. +.TP +.B \-B +Bookmark��ɽ�����롥 +.TP +.BI \-bookmark\ file +Bookmark �Υե��������ꤹ�롥 +.TP +.B \-M +���顼ɽ���ʤ��� +.TP +.B \-F +�ե졼���ưɽ�����롥 +.TP +.B \-S +Ϣ³������Ԥ�1�ԤˤޤȤ��ɽ�����롥 +.TP +.B \-X +.B w3m +��λ���ˡ������β��̤����ʤ��� +.TP +.B \-W +�ޤ��֤���������Ȥ����ɤ������ڤ꤫���롥 +.TP +.BI \-o\ option=value +���ץ�������ꤹ�롥 +.TP +.B \-no\-proxy +�ץ����������Ѥ��ʤ��� +.TP +.B \-no\-mouse +�ޥ��������Ѥ��ʤ��� +.TP +.B \-cookie +���å�����������롥 +.TP +.B \-no\-cookie +���å�����������ʤ��� +.TP +.B \-num +���ֹ��ɽ�����롥 +.TP +.B \-dump +URL�����Ƥ��ɤߤ��ߡ��������줿�Хåե������Ƥ�ɸ����Ϥ˽Ф��� +ʸ�������80��Ȳ��ꤵ��롥�������ϡ����� +.B \-cols +���ץ������ѹ���ǽ�� +.TP +.BI \-cols\ �� +.B \-dump +���ץ�����Ȥ����ˡ�ʸ���������ꤹ�롥 +.TP +.BI \-ppc\ �ԥ������ +ʸ����������ꤹ�롥�ǥե���Ȥ� 8.0�� +.TP +.B \-dump_source +URL�����Ƥ��ɤߤ��ߡ�����������ɸ����Ϥ˽Ф��������������Ѵ��⤵��ʤ��� +.TP +.B \-dump_head +URL�˥������������إå��������Ϥ��롥 +.SS ʸ���ɽ������ +HTMLʸ���ɽ�����Ƥ���Ȥ��ˤϡ����Τ褦��ɽ���ˤʤ�ޤ��� +.in +8n +.TS +box tab(;); +l|c|c +l|c|c +l|c|c +l|c|c. +;���顼ɽ����;���ɽ���� +_ +���;�Ŀ�;���� +����饤�����;�п�;ȿžɽ�� +FORM��������ʬ;�ֿ�;ȿžɽ�� +.TE +.PP +���顼ɽ�����ο��ϡ����ץ��������ѥͥ� +.B o +���ѹ����뤳�Ȥ��Ǥ��ޤ��� +.SS ��ư��λȤ����� +��ư������ϡ�1ʸ���Υ��ޥ�ɤ��ܡ��ɤ������Ϥ��뤳�Ȥ� +.B w3m +�����ޤ��� +.PP +���ޥ�ɤˤϼ��Τ褦�ʤ�Τ�����ޤ����ʲ��ε��ҤǤϡ� +.B C-x +�ϥ���ȥ�����x��ɽ���ޤ����ޤ��� +.B SPC +�ϥ��ڡ����С��� +.B RET +�ϥ������ +.B ESC +�ϥ��������ץ����Ǥ��� +.PP +�����ǽƤ���Τϡ����ꥸ�ʥ��ǤΥ������Ǥ��� +.\" \fIlynx\fr(1) +.\" ���Υ�������Ѥ˥���ѥ��뤷�Ƥ����ΤˤĤ��Ƥϡ� +.\" \fIw3m_lynx(1) +.\" ��������� +.SS �ڡ���/���������ư +.TP 1i +.B SPC, C-v +���Υڡ�����ɽ�����ޤ��� +.TP +.B b, "ESC v" +���Υڡ�����ɽ�����ޤ��� +.TP +.B l, C-f, ��������� +��������˰�ư���ޤ��� +.TP +.B h, C-b, ��������� +��������˰�ư���ޤ��� +.TP +.B j, C-n, ��������� +��������˰�ư���ޤ��� +.TP +.B k, C-p, ��������� +����������˰�ư���ޤ��� +.TP +.B J +���̤�1�Ծ�˥��������뤷�ޤ��� +.TP +.B K +���̤�1�Բ��˥��������뤷�ޤ��� +.TP +.B w +����ñ��˰�ư���ޤ��� +.TP +.B W +����ñ��˰�ư���ޤ��� +.TP +.B > +�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +.TP +.B < +�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +.TP +.B ". " +�������Τ�1ʸ�����ˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +.TP +.B ", " +�������Τ�1ʸ�����ˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +.TP +.B g +ʸ��Τ����Ф��ιԤ˰�ư���ޤ��� +.TP +.B G +ʸ��Τ����ФιԤ˰�ư���ޤ��� +.TP +.B "ESC g" +���̲��ǹ��ֹ�����Ϥ��������ǻ��ꤷ���Ԥ˰�ư���ޤ��� +������ +.$ +�����Ϥ���ȡ��ǽ��Ԥ˰�ư���ޤ��� +.TP +.B TAB +���Υ�˰�ư���ޤ��� +.TP +.B C-u, "ESC TAB" +���Υ�˰�ư���ޤ��� +.SS �ϥ��ѡ������� +.TP +.B RET +���ߥ������뤬�������ؤ����ʸ����ɤߤ��ߤޤ��� +.TP +.B a, "ESC RET" +���ߥ������뤬�������ؤ����ʸ���ե��������¸���ޤ��� +.TP +.B u +���ߥ������뤬�������ؤ����URL��ɽ�����ޤ��� +.TP +.B I +���ߥ������뤬�������б����������ɽ�����ޤ��� +.TP +.B "ESC I" +���ߥ������뤬�������ؤ�������ե��������¸���ޤ��� +.TP +.B ":" +URL����ʸ������ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +.TP +.B "ESC :" +Message-ID����ʸ�����news: �Υ�ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +.TP +.B c +���ߤ�ʸ���URL��ɽ�����ޤ��� +.TP +.B = +���ߤ�ʸ��˴ؤ�������ɽ�����ޤ��� +.TP +.B F +<FRAMESET>��ޤ�ʸ���ɽ�����Ƥ���Ȥ��ˡ�<FRAME>�����λؤ�ʣ����ʸ���1�Ĥ� +ʸ����Ѵ�����ɽ�����ޤ��� +.TP +.B M +���߸��Ƥ���ڡ��������֥饦����Ȥä�ɽ�����ޤ��� +.B 2M, 3M +��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +.TP +.B "ESC M" +���ߤΥ��������֥饦����Ȥä�ɽ�����ޤ��� +.B "2ESC M", "3ESC M" +��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +.El +.SS �ե������URL�ط������ +.TP +.B U +URL����ꤷ�Ƴ����ޤ��� +.TP +.B V +��������ե��������ꤷ�Ƴ����ޤ��� +.TP +.B @ +���ޥ�ɤ�¹Ԥ�����̤������ɤ�Ǥ���ɽ�����ޤ��� +.TP +.B # +���ޥ�ɤ�¹Ԥ�����̤��ɤߤ��ߤʤ���ɽ�����ޤ��� +.SS �Хåե���� +.TP +.B B +���߸��Ƥ���Хåե���������������ΥХåե���ɽ�����ޤ��� +.TP +.B v +HTML�Υ�������ɽ�����ޤ��� +.TP +.B s +�Хåե�����⡼�ɤ�����ޤ��� +.TP +.B E +���߸��Ƥ���Хåե�����������ե�����ξ�硤���Υե�����ǥ������Խ����� +�������ǥ�����λ�����塤���Υե����������ɤ߹��ߤޤ��� +.TP +.B R +�Хåե�������ɤ߹��ߤޤ��� +.TP +.B S +�Хåե���ɽ�����Ƥ�ե��������¸���ޤ��� +.TP +.B "ESC s" +HTML�Υ�������ե��������¸���ޤ��� +.v +�ǥ�������ɽ������ +.S +����¸����ΤȤۤ�Ʊ���Ǥ����� +.B "ESC s" +����¸�����ե�����ϴ��������ɤ����ꥸ�ʥ�ΤޤޤǤ���Τ��Ф��ơ� +.B "v S" +����¸����ȸ���ɽ���˻ȤäƤ�����������ɤ��Ѵ��������¸����ޤ��� +.TP +.B "ESC e" +����ɽ������Ƥ���Хåե���ɽ������Ƥ�������Τޤޥ��ǥ������Խ����ޤ��� +.SS �Хåե�����⡼�� +.B s +�ǥХåե�����⡼�ɤ����ä��Ȥ��Υ������Ǥ��� +.TP +.B k, C-p +��ľ�ΥХåե������ޤ��� +.TP +.B j, C-n +��IJ��ΥХåե������ޤ��� +.TP +.B D +�������Ƥ���Хåե��������ޤ��� +.TP +.B RET +�������Ƥ���Хåե���ɽ�����ޤ��� +.El +.SS �֥å��ޡ������ +.TP +.B "ESC b" +�֥å��ޡ������ɤ߹��ߤޤ��� +.TP +.B "ESC a" +���߸��Ƥ���ڡ�����֥å��ޡ������ɲä��ޤ��� +.SS ���� +.TP +.B /, C-s +���ߤΥ���������֤���ե����������˸����ä�����ɽ�������ޤ��� +.TP +.B ?, C-r +���ߤΥ���������֤���ե��������Ƭ�˸����ä�����ɽ�������ޤ��� +.TP +.B n +�������ޤ��� +.SS �ޡ������ +.TP +.B C-SPC +�ޡ��������������ޤ����ޡ�����ȿžɽ������ޤ��� +.TP +.B P +������Υޡ����˰�ư���ޤ��� +.TP +.B N +��ĸ�Υޡ����˰�ư���ޤ��� +.TP +.B "\"" +����ɽ���ǻ��ꤵ�줿ʸ��������ƥޡ������ޤ��� +.SS ����¾ +.TP +.B ! +�����륳�ޥ�ɤ�¹Ԥ��ޤ��� +.TP +.B H +�إ�ץե������ɽ�����ޤ��� +.TP +.B o +���ץ��������ѥͥ��ɽ�����ޤ��� +.TP +.B C-c +ʸ����ɤ߹��ߤ����Ǥ��ޤ��� +.TP +.B C-z +�����ڥ�ɤ��ޤ��� +.TP +.B q +.B w3m +��λ���ޤ������ץ���������ˤ�äơ���λ���뤫�ɤ�����ǧ���ޤ��� +.TP +.B Q +��ǧ������ +.B w3m +��λ���ޤ��� +.SS ���Խ� +���̤κDz��Ԥ�ʸ��������Ϥ������ͭ���ʥ������Ǥ��� +.TP +.B C-f +��������˰�ư���ޤ��� +.TP +.B C-b +��������˰�ư���ޤ��� +.TP +.B C-h +���������ľ����ʸ���������ޤ��� +.TP +.B C-d +����������֤�ʸ���������ޤ��� +.TP +.B C-k +����������֤����������ޤ��� +.TP +.B C-u +����������֤������������ޤ��� +.TP +.B C-a +ʸ�������Ƭ�˰�ư���ޤ��� +.TP +.B C-e +ʸ����κǸ�˰�ư���ޤ��� +.TP +.B C-p +�ҥ��ȥ꤫��������ʸ�������Ф��ޤ��� +.TP +.B C-n +�ҥ��ȥ꤫�鼡��ʸ�������Ф��ޤ��� +.TP +.B TAB, SPC +�ե�����̾���ϻ��ˡ��ե�����̾���䴰���ޤ��� +.TP +.B RET +���Ϥ�λ���ޤ��� +.SH SEE ALSO +.I kterm\fR(1), +.\" .I lynx\fR(1), +.\" .I w3m_lynx\fR(1), +.I tty\fR(4) +.SH AUTHOR +��ƣ ��§ +.br +aito@ei5sun.yz.yamagata-u.ac.jp + diff --git a/doc/FAQ.html b/doc/FAQ.html new file mode 100644 index 0000000..7cfa173 --- /dev/null +++ b/doc/FAQ.html @@ -0,0 +1,278 @@ +<HTML> +<HEAD> +<TITLE>W3M FAQ</TITLE> +</HEAD> +<BODY> +<p> +<center><h1>Frequently Asked Questions and Answers about w3m</h1></center> +<div align=right> +Akinori Ito<br> +aito@ei5sun.yz.yamagata-u.ac.jp<br> +Corrected by Tom Berger <tom.be@gmx.net> +</div> +<p> +<b><center><font size=+1><u><a name="index">Index</a></u></font></center></b> +<p> +<br> +<ul> +<li><h2><a href="#general">General Questions, How to Get It, Required Environment</a></h2></li> +<ul> +<li><h3>How do I pronounce "w3m"?</h3> +<li><h3>Why is it called "w3m"?</h3> +<li><h3>On which platforms does w3m work?</h3> +<li><h3>Where can I get more information about w3m?</h3> +<li><h3>Is there a mailing list for w3m?</h3> +<li><h3>Are there any binary distributions?</h3> +</ul> +<br> +<li><a href="#install"><h2>Compile and Install</h2></a> +<br> +<li><a href="#command"><h2>Options, Commands, Usage</h2></a> +<ul> +<li><h3>w3m quits if started without parameters. What's wrong?</h3> +<li><h3>w3m starts with black characters on a black screen. How do I change this?</h3> +<li><h3>Does w3m support colours?</h3> +<li><h3>Does w3m support monochrome display?</h3> +<li><h3>How do I shift the display?</h3> +<li><h3>How do I move from anchor to anchor?</h3> +<li><h3>Netscape displays a word red, but w3m doesn't. Why?</h3> +<li><h3>How do I change the colour of anchor-/image-/form links?</h3> +<li><h3>w3m doesn't seem to use the variable EDITOR. Why? </h3> +<li><h3>How do I quit a search or URL text input?</h3> +</ul> +<br> +<li><a href="#www"><h2>Questions about WWW usage</h2></a> +<ul> +<li><h3>How do I fill in forms with w3m?</h3> +<li><h3>Seems like w3m is slower than Netscape or Lynx. Why?</h3> +<li><h3>Loading time doesn't decrease when loading a previously seen page</h3> +<li><h3>How do I download a linked file?</h3> +<li><h3>How do I specify a proxy server?</h3> +<li><h3>w3m freezes when I invoke an external browser.</h3> +<li><h3>How do I change the default image viewer?</h3> +<li><h3>How do I enter a URL?</h3> +<li><h3>w3m appends a URL to the former one despite of having cleared the line with Ctrl-u. What to do?</h3> +</ul> +<br> +<li><a href="other"><h2>Misc</a></h2> +<ul> +<li><h3>What is w3m's configuration file?</h3> +<li><h3>What are these w3mxxxx files in my ~/.w3m directory for?</h3> +</ul> +<br> +<br> +<u><h2><a name="general">General Questions, How to Get It, Required Environment</a></h2></u> +<br> +<dl> +<dt><h3>How do I pronounce "w3m"?</h3> +<dd>It's "W-three-M". It doesn't rhyme with "pteranodon". +<p> +<dt><h3>Why is it called "w3m"?</h3> +<dd>It's an abbreviation of "WWW-wo-Miru", which is Japanese for +"See the WWW". So in English the name of this browser would be +something like "stw3". +<p> +<dt><h3>On which platforms does w3m work?</h3> +<dd>It runs on various versions of Unix, since version 990226 on OS/2 and since +version 990303 also on MS-Windows with Cygwin32. +<br> +Current versions have been confirmed to run on: +<ul> +<li>Solaris 2.5 +<li>SunOs 4.1.x +<li>HP-UX 9.x and 10.x +<li>Linux 2.0.30 and 2.2.9 with glibc 2.1 +<li>FreeBSD 2.2.8 and 3.1 +<li>EWS4800 Release 12.2 Rev.A +</ul> +<dt><h3>Where can I get more information about w3m?</h3> +<dd>At the <a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/">English w3m home page</a>. +<dt><h3>Is there a mailing list for w3m?</h3> +<dd>There is a mailing list for developpers (w3m-dev-en). Please see +<a href="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/">w3m page</a> +for details. You may also mail your comments to <a href="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">the author</a>. +<dt><h3>Are there any binary distributions?</h3> +<dd>So far there are only binaries for the win/cygnus32 version. You can get +them from <a href="ftp://ei5nazha.yz.yamagata-u.ac.jp/w3m/binaries">here</a>. +Contact <a href="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">the author</a> if you want to contribute binaries for other platforms. +</dd> +</dl> +<br> +<div align=right> +<i>Up to <a href="#index">index</a></i> +</div> +<br> +<u><h2><a name="install">Compile and Install</a></h2></u> +No problem :-) +<u><h2><a name="command">Options, Commands, Usage</a></h2></u> +<br> +<dl> +<dt><h3>w3m quits if started without parameters. What's wrong?</h3> +<dd>w3m is a <b>pager</b>. Therefore it just quits when invoked without any +arguments. Possible arguments are: +<ol> +<li>A filename or an URL +<li>Pipe from standard input +<li>The -B option (Show bookmark file) +<li>From a specified HTTP_HOME or WWW_HOME variable +</ol> +<p> +<dt><h3>w3m starts with black characters on black background. How do I change +this?</h3> +<dd> +When compiled with colour support, w3m assumes a white background and therefore +displays black characters. +<br> +You may either change the background colour of your terminal (e.g. with the -bg +option in a xterm) or take these steps: +<ul> +<li>invoke w3m with 'w3m -M' (for monochrome), +<li>type 'o' for getting to the options screen +<li><b>Mark 'Display with colour' as ON</b> and choose an arbitrary colour. +Click on [OK]. +</ul> +<p> +<dt><h3>Does w3m support colours?</h3> +<dd>Yes. When you run './configure', answer the question +<p> +<pre> +Let's do some configurations. Choose config option among the list." + +1 - Baby model (no color, no menu, no mouse, no cookie, no SSL) +2 - Little model (color, menu, no mouse, no cookie, no SSL) +3 - Mouse model (color, menu, mouse, no cookie, no SSL) +4 - Cookie model (color, menu, mouse, cookie, no SSL) +5 - Monster model (with everything; you need openSSL library) +6 - Customize +Which? +</pre> +<p> +with 2,3,4 or 5. +<p> +<dt><h3>Does w3m support monochrome display?</h3> +<dd>Yes. You may either +<ol> +<li>Answer the above mentioned 'configure' question with 1, or +<li>Invoke w3m with the -M option, or +<li>Type 'o' within w3m to enter the options screen and turn off colour display +mode. +</ol> +<dt><h3>How do I shift the display?</h3> +<dd>You can shift the display by moving the cursor to the edge of the screen. You +may also use the ">"/"<" or "."/"," keys. +<br> +Another idea would be adjusting the xterm with the -geometry option (e.g. +something like 'xterm -geometry 110x45 -bg white -name w3m -e w3m -B'). +<dt><h3>How do I move from anchor to anchor?</h3> +<dd>You can move to the next anchor using TAB. ESC TAB moves cursor to the previous anchor. +<p> +<dt><h3>Netscape displays a word red, but w3m doesn't. Why?</h3> +<dd>w3m doesn't support <FONT COLOR=".."> tags. It won't be impossible to implement this, but I think it would make the document more difficult to read. +<p> +<dt><h3>How do I change the colour of anchor-/image-/form links?</h3> +<dd>Type 'o' within w3m to get the 'options' screen. You can change these +settings there. +<dt> +<dt><h3>w3m doesn't seem to use the variable EDITOR. Why? </h3> +<dd><dd>Go to the 'options' screen using the "o" key. Any entry in the 'Editor' field will override the environment variable. +<br> +If you want to use the editor specified by EDITOR blank the field and push [OK]. +<p> +<dt><h3>How do I quit a search or URL text input?</h3> +<dd>Clear input text using Ctrl-u and hit RETURN. +</dd> +</dl> +<br> +<div align=right> +<i>Up to <a href="#index">index</a></i> +</div> +<br> +<u><h2><a name="www">Questions about WWW usage</a></h2></u> +<br> +<dl> +<dt><h3>How do I fill in forms with w3m?</h3> +<dd>Form input fields are displayed in red (or reverse). Move the cursor to +them and hit RETURN. Then, +<ul> +<li>if it is a text input field, put in your text on the bottom line, +<li>if it is a radiobutton or checkbox, that item is selected, +<li>if it is a textarea, an editor is spawned, +<li>if it is 'submit' or 'reset', well, just do it. +</ul> +<dt><h3>Seems like w3m is slower than Netscape or Lynx. Why?</h3> +<dd>w3m renders a HTML document in two passes. Therefore it displays the documentnot before having read the entire document. +<br> +Netscape or Lynx display the document before having read the whole page, +and therefore seem faster. +<p> +<dt><h3>Loading time doesn't decrease when loading a previously seen page</h3> +<dd>w3m doesn't have its own cache. Therefore, it reads the document +from the server each time it accesses it. If possible, use a cache server. +<p> +<dt><h3>How do I download a linked file?</h3> +<dd>Use 'a' (or 'd' with Lynx-like keybindings) or ESC RET. If you want to download an inline image, use ESC 'I'. +<p> +<dt><h3>How do I specify a proxy server?</h3> +<dd>Set the environment variable HTTP_PROXY or use the option setting panel +("o" key). For example, if you want to use port 8000 of proxy.hogege.com, specify +<p> +<pre> + http://proxy.hogege.com:8000/ +</pre> +<p> +<dt><h3>w3m freezes when I invoke an external browser.</h3> +<dd>Enter w3m's option screen using the 'o' key and specify +<p> +<pre> + netscape %s & +</pre> +<p> +(if you are using netscape). +<p> +<dt><h3>How do I change the default image viewer?</h3> +<dd>By default w3m uses xv to view images. If you want to change it into, let's say, 'display', add the following line to ~/.mailcap or /etc/mailcap. +<p> +<pre> +image/*; display %s +</pre> +<p> +You can specify external viewers of other file types as well: +<p> +<pre> +image/*; display %s +application/postscript; ghostview %s +application/x-dvi; xdvi %s +</pre> +<dt><h3>How do I enter a URL?</h3> +<dd>Type SHIFT-U +<p> +<dt><h3>w3m appends a URL to the former one despite of having cleared the line +with Ctrl-u. What to do?</h3> +<dd>Enter the <i>complete</i> adress, e.g. http://www.slashdot.org. +</dd> +</dl> +<br> +<div align=right> +<i>Up to <a href="#index">index</a></i> +</div> +<br> +<u><h2><a name="other">Miscellanous</a></h2></u> +<br> +<dl> +<dt><h3>What is w3m's configuration file?</h3> +<dd>It is ~/.w3m/config. +<p> +<dt><h3>What are these w3mxxxx files in my ~/.w3m directory for?</h3> +<dd>These are temporary files used by w3m when reading documents from a +WWW server. They are not cache files and are usually deleted when w3m is +terminated. If there remain any temp files, please remove them by yourself. +<p> +</dd> +</dl> +<br> +<div align=right> +<i>Up to <a href="#index">index</a></i> +</div> +<br> +</BODY> +</HTML> diff --git a/doc/HISTORY b/doc/HISTORY new file mode 100644 index 0000000..00ca8a0 --- /dev/null +++ b/doc/HISTORY @@ -0,0 +1,1989 @@ +2001/3/23 ============================================================== +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01807] Re: w3m-0.2.0 +* url.c doesn't compile when USE_NNTP or __EMX__ is defined. +* patch for EWS4800 +* when #define USE_SSL and #undef USE_SSL_VERIFY, rc.c and url.c + doesn't compile. (problems about ssl_forbid_method) +* when saveBufferDelNum and del==TRUE, patterns before ":" are + deleted twice. +* bugfix about saving history. + +From: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp> +Subject: [w3m-dev 01810] deflate (was: w3m-0.2.0) +deflate patch in 0.2.0 doesn't work on http://cvs.m17n.org/~akr/diary/ . + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01808] Re: w3m-0.2.0 +w3m doesn't compile on GNU/Linux and glibc2.2 because it lacks +sin.ss_len. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00399] Re: w3m-0.2.0 + >> From: Dan Fandrich <dan@coneharvesters.com> + >> Version 0.2.0 still contains the following bugs which I fixed two months + >> ago and sent patches for to this list, namely: + >> - core dumps on startup if given a URL requiring a needsterminal mailcap + >> handler + >> - destroys most of an existing ~/.mailcap file without warning when editing + >> - mailcap handling is still wrong as MIME type should be case insensitive + >> - private mailcap extension has an illegal name + +From: SATO Seichi <seichi@as.airnet.ne.jp> +Subject: w3m regex bugs +w3m coredumps when passing $* as a search string. + +2001/3/22 ============================================================== + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01664] Re: Patch for anonymizer.com +Don't call cleanupName() when the URL is http://<host>/<scheme>: ... + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01670] Re: w3m-0.1.11-pre-kokb24-test1 +strcpy/strncpy in Str.c are replaced with bcopy. + +From: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp> +Subject: [w3m-dev 01618] backend patch +Subject: [w3m-dev 01671] backend patch for w3m-0.1.11-pre-kokb24-test1 +A patch for w3m to work as a client. (-backend patch) + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01673] SEGV in append_frame_info() +Improvement of illegal frame handling. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01674] image map +w3m doesn't follow anchors from client-side image map when the URLs +are like "#test". + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01675] goto label +Changed w3m not to reload the document when following label-only URL +like #label. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01676] Re: w3m-0.1.11-pre-kokb24-test1 +Subject: [w3m-dev 01678] Re: w3m-0.1.11-pre-kokb24-test1 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01680] Re: w3m-0.1.11-pre-kokb24-test1 +To remove the compiler warnings + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01684] Re: http://cvs.m17n.org/~akr/diary/ +application/x-deflate support. + +From: Moritz Barsnick <barsnick@gmx.net> +Subject: [w3m-dev-en 00318] Information about current page +Subject: [w3m-dev-en 00320] Re: Information about current page +Subject: [w3m-dev-en 00322] Re: Information about current page +Subject: [w3m-dev-en 00323] Buglet (Was: Re: Information about current page) +Changes 'URL of the current anchor' on the info page into +'full' URL. When the cursor is on a form element, +`Method/type of current form' will be displayed. + +From: c603273@vus069.trl.telstra.com.au (Brian Keck) +Subject: [w3m-dev-en 00343] patch for proxy user:passwd on command line +Subject: [w3m-dev-en 00351] Re: patch for proxy user:passwd on command line +This patch to w3m-0.1.11-pre-kokb23 adds the lynx-like option + + -pauth username:password + +so I don't have to retype username & password every time I run w3m, +which is often. It's so simple I wonder whether it's against policy, +but it would be nice for me & some others if it was in the official +0.1.11. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01772] Re: visited anchor +Subject: [w3m-dev 01773] Re: visited anchor + * visited anhor color support. + * textlist based history implementation. + * history URLs are stored in a hash table. + * the implementation of rules are changed. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01786] Re: w3m-0.1.11-pre-hsaka24 +Subject: [w3m-dev 01787] Re: w3m-0.1.11-pre-hsaka24 + * Improvement of illegal frame handling. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01788] Re: w3m-0.1.11-pre-hsaka24 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01792] Re: w3m-0.1.11-pre-hsaka24 +search algorithm in retrieveAnchor() is changed from linear search +to binary search. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01793] <li type=".."> +make type attribute of <li> tag effective not only for the <li> +element but also for all <li> tags that follows the first +type-specified <li> tag. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01801] some fixes. +Bugfix of frame + +Subject: IPv6 support for w3m's ftp +From: Hajimu UMEMOTO <ume@imasy.or.jp> +IPv6 support for FTP. + +2001/3/16 ============================================================= +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01711] Authorization +* http://user:pass@hostname/ support. + +From: hsaka@mth.biglobe.ne.jp +Subject: [w3m-dev 01724] buf->type when mailcap is used. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01726] anchor jump too slow by TAB-key on STDIN. +* when moving from anchor to anchor by TAB on the document read + from stdin, the movement is very slow because currentdir() is invoked + on each TAB. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01727] C-z when stdin + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01729] ignore_null_img_alt +* when ignore_null_img_alt is OFF, no img link is displayed when + no ALT attribute is specified. + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01730] Re: <hr> in a table +Improvement of <hr>. + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Subject: [w3m-dev 01731] completion list +When completing a filename, the candidates of the completion +will be displayed like this: + +----- Completion list ----- +X11R6/ compat/ include/ libdata/ local/ nfs/ ports/ share/ +bin/ games/ lib/ libexec/ mdec/ obj/ sbin/ src/ +(Load)Filename? /usr/ + + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01733] A patch concerning SSL +The following two improvements are done about SSL: +1. a new option ``ssl_forbid_method'' is added. +2. an error message is displayed when w3m fails to establish an + SSL connection. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01735] Re: A patch concerning SSL +Subject: [w3m-dev 01737] Re: A patch concerning SSL +1. the data type of ssl_forbid_method is changed from P_STRING to P_SSLPATH. +2. Error message log function. + +From: kiwamu <kiwamu@debian.or.jp> +Subject: [w3m-dev 01739] wheel mouse patch + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01742] w3mmee 0.1.11p16-6 segfault +w3mmee 0.1.11p16-6 segfaults depending on the content of mime.types. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01752] SEGV in search_param() + > >> * w3m -o 1 causes SEGV. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01753] empty <select> +When <select>..</select> have no <option>, for example + +<form action=A> +<select name=B></select> +<input type=submit> +</form> + +submit causes SEGV. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01754] A search does not stop. +When reading a large file from stdin and wrap search option is ON, +a search that doesn't hit will cause an infinite loop. + +From: WATANABE Katsuyuki <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01755] relative path with -bookmark option +* when a bookmark file name is given by -bookmark as relative path, + `add to bookmark'doesn't work. + +2001/2/7 +From: aito +Subject: [w3m-dev 01722] <hr> in a table +* the width of <hr> in a table exceeds the column width. + +2001/2/6 +From: aito +* `Local cookie' mechanism is introduced to authorize local CGI. + The behavior of CGI script using the local cookie is as follows: + - w3m generates process-dependent `Local cookie' + - on the local CGI invocation, w3m passes the script the local + cookie through the environment variable LOCAL_COOKIE. + - the sctipt embeds the local cookie into the form for the next + local CGI invocation. + - on the next CGI invocation, the CGI script compares two local CGIs + which are passed through CGI parameter and environment variable. + If they are different, the script prohibits dangerous operations, + such as file creation and file deletion. + +* The local cookie mechanism is implemented on w3mbookmark and + w3mhelperpanel. + +* On Linux, gcmain.c doesn't compile when the GC library is already + installed in /usr/local/lib. + +2001/1/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01667] Re: mailer %s + +2001/1/24 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01661] Re: <head> + security fix. + + +2001/1/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * check if ", <, > and &s are quoted within attribute values. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01663] replace addUniqHist with addHist in loadHistory() + + +2001/1/22 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01617] Re: first body with -m (Re: w3m-m17n-0.7) + * terminal resize related fix. + * info page ('=' command) fix for multi-layered frame page. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01621] NEXT_LINK and GOTO_LINE problem + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01623] Re: (frame) http://www.securityfocus.com/ +Subject: [w3m-dev 01632] Re: (frame) http://www.securityfocus.com/ + frame fix. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01624] Re: first body with -m +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01625] Re: first body with -m + pgFore, pgBack behaviour fix. + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01635] Directory list + local.c directory list fix. + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01643] buffername +Subject: [w3m-dev 01650] Re: buffername + buffername (title) related improvements. + * when displayLink is ON, truncate buffername on showing long URL. + * displayBuffer() cleanup. + * remove trailing spaces from content of <title>..</title>. + * [w3m-dev 01503], [w3m-dev 01504] + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01646] putAnchor + * putAnchor related improvement. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01647] Re: first body with -m + * cursor position moves unexpectedly when reloading a URL + with #label. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01651] display column position with LINE_INFO + +2001/1/5 + +From: Ryoji Kato <ryoji.kato@nrj.ericsson.se> +Subject: [w3m-dev 01582] rfc2732 patch + literal IPv6 address treatment (bracketed by '[' and ']') + according to RFC2732. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01594] first body with -m (Re: w3m-m17n-0.7) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01602] Re: first body with -m (Re: w3m-m17n-0.7) + +2001/1/1 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01584] Re: attribute replacing in frames. (Re: some fixes) + + +2000/12/27 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * remove trailing blank lines in a buffer. + +2000/12/26 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01560] Re: long URL + Multiple 'u' and 'c' scrolls long URL. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01570] Re: long URL + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01506] compile option of gc.a + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01509] Forward: Bug#79689: No way to view information on SSL certificates + Now '=' shows info about SSL certificate. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01556] Re: ANSI color support (was Re: w3m-m17n-0.4) + ANSI color support. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01535] how to check wait3 in configure. +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01537] Re: how to check wait3 in configure. + On BSD/OS 3.1, SunOS 4.1.3, configure can't detect wait3(). + +2000/12/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + <plaintext> doesn't work. + + +2000/12/22 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01555] Re: some fixes for <select> + w3m crashes by <select> without <option>. + +2000/12/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table related improvements (<xmp> inside table, etc) + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01536] Re: <P> in <DL> +Subject: [w3m-dev 01544] Re: <P> in <DL> + * w3m crashes by an illegal HTML. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * treat unclosed <a>, <img_alt>, <b>, <u> + +2000/12/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * bugfix on <dt> tag processing in feed_table_tag(). + * w3m eventually crashed when a tag is not closed in a table. + * ignore <p> just after <dt>. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01530] returned at a morment. + * skip newline within "-enclosed attribute value. + +Subject: [w3m-dev 01531] coocie check in header from stdin. + * w3m crashes by 'cat mail | w3m -m' + + +2000/12/17 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01513] Re: w3m-0.1.11-pre-kokb23 + frame.c bugfix +Subject: [w3m-dev 01515] some fixes for <select> +Subject: [w3m-dev 01516] Re: some fixes for <select> + Several improvements on <select>..<option> + +2000/12/14 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01501] Re: w3m-0.1.11-pre-kokb23 + Compile error for 'no menu' model + + +2000/12/13 + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01483] Patch to show image URL includes anchor + Peek image URL by 'i' + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01500] fix risky code in url.c + Vulnerble code in url.c fixed + +2000/12/12 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01491] bug ? + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + Search for a string that contains null character. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01498] Re: null character + Infinite loop + + +2000/12/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * StrmyISgets doesn't recognize a '\r' as a newline character. + * Null character support on pager mode. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01487] A string in <textarea> is broken after editing + <textarea> related fix. + +Subject: [w3m-dev 01488] buffer overflow bugs + * Buffer overflow fixes. + +2000/12/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * maximum_table_width now considers width attribute in td and th tag. + +2000/12/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01473] Re: internal tag and attribute check + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * hborder and border attribute handling. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01478] Option Setting Panel + * Improvement of the option setting panel view. + +2000/12/7 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * parse_tag improvements. + * don't parse tag within visible_length(). + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01456] linein.c + * linein.c is rewritten based on calcPosition(). + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01457] cursor position on sumbit form + * TAB key behaviour fix. + +2000/12/3 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01449] Re: Directory of private header of gc library. + * w3m crashes when accessing a list after popping the last element + by popText (rpopText). + +2000/12/2 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * image map related fix. + +2000/12/1 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: Security hole in w3m (<input_alt type=file>) + * Prohibit using internal tags in HTML. + +Subject: [w3m-dev 01432] Re: w3m-0.1.11-pre-kokb22 patch +Subject: [w3m-dev 01437] Re: w3m-0.1.11-pre-kokb22 patch + * Image map related fix. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * a compile option which enables the removal of trailing + blank lines in a burrer. (ENABLE_REMOVE_TRAILINGSPACES) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00301] Re: "w3m -h" outputs to stderr + * Destination of w3m -h output is changed from stderr + to stdout. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01430] Re: w3m-0.1.11-pre-kokb22 patch + * EWS4800(/usr/abiccs/bin/cc) support. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * id attribute check in dummy_table tag. + * fid attribute check in form_int tag. + * table stack overflow check. + +2000/11/29 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01422] bpcmp in anchor.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01423] Re: bpcmp in anchor.c + * some improvements for speedup. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * CheckType() bugfix and speedup. + +2000/11/28 + +From: Takenobu Sugiyama <sugiyama@ae.advantest.co.jp> +Subject: patch for cygwin + * enables ftp download for cygwin w3m + +2000/11/27 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01401] Re: bugfix of display of control chars, merge of easy UTF-8 patch, etc. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table rendering speed-up. + + +2000/11/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table column width sometimes get narrower than specified width value. + +2000/11/24 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Progress bar display enhancement. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * command line option about proxy and cookie doesn't work. + * 'Save to local file' overwrites the existing file. + +2000/11/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * get_ctype is now macro. + * menu.c type mismatch fix. + + +2000/11/22 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * fixes for speedup. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01372] w3m sometimes uses the wrong mailcap entry + http://bugs.debian.org/77679 + +2000/11/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * an empty table in another table makes the outer table funny. + +2000/11/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + gc6 support. + +2000/11/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * space characters in a buffer are mapped into 0x80-0x9f. + * unprintable characters (0x80-0xa0) are displayed as \xxx. + +From: Tsutomu Okada ($B2,ED(B $BJY(B) <okada@furuno.co.jp> +Subject: [w3m-dev 01354] minimize when #undef USE_GOPHER or USE_NNTP + +2000/11/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + getescapechar() returns abnormal value for illegal entity. + +2000/11/15 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table-related fix. + * DEL character is treated as `breakable space.' + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01338] Re: Lynx patch for character encoding in form +Subject: [w3m-dev 01342] Re: Lynx patch for character encoding in form + * support for accept-charset attribute in form tag. + +2000/11/14 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * cleanup_str and htmlquote_str now returns the oritinal string + itself when there's no need to (un)quote it. + +2000/11/10 + +From: Katsuyuki Watanabe <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01336] patch for Cygwin 1.1.x + Patch for Cygwin 1.1.x (1.1.3 and later) + +2000/11/8 + +From: Jan Nieuwenhuizen <janneke@gnu.org> +Subject: [w3m-dev-en 00189] [PATCH] w3m menu <select> search + Enable to search within popup menu. + + +2000/11/7 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01331] Re: form TEXT: + * Search string history and form input string history are merged. + +2000/11/4 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * When a cell width exceeds the screen width, format contents in + the cell to fit into the screen width. + +2000/11/2 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01313] Re: SCM_NNTP + nntp: for MARL_URL + +2000/10/31 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01310] Re: option select (Re: w3mmee-0.1.11p10) + Output error messages from gc library using disp_message_nsec. + +2000/10/30 + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01294] mouse no effect on blank page. +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01295] Re: mouse no effect on blank page. + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 01297] Re: backword search bug report + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01298] Re: backword search bug report + bug fix of backword search +Subject: [w3m-dev 01299] Re: backword search bug report + bug fix of the handling of multi-byte regexp. + +2000/10/29 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * LESSOPEN can be set via the option setting panel. (default: off) + * speed-up of gunzip_stream(), save2tmp(), visible_length(). + + +2000/10/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Emacs-like completion key support. + (by #define EMACS_LIKE_LINEEDIT in config.h) + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01284] Re: improvement of filename input + * in 'Goto URL' command, local file name will be completed + after 'file:/'. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01280] Stop to prepend rc_dir to full path. + +2000/10/27 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01269] Re: SCM_NNTP +Subject: [w3m-dev 01273] Re: SCM_NNTP + Prohibit gopher:, news: and nntp: scheme when USE_GOPHER and USE_NNTP + macros are undefined. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01258] improvement of filename input + * Completion lists are displayed by C-d. + * in 'Goto URL' command, local file name will be completed + after 'file:/', 'file:///' and 'file://localhost/'. + * password part of URLs in the history list are removed. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01277] Accept-Encoding: gzip (Re: some wishlists) + Accept-Encoding: gzip, compress + is appended in the request header. +Subject: [w3m-dev 01275] Re: squeeze multiple blank lines option ( http://bugs.debian.org/75527 ) + when #ifdef DEBIAN, + 'squeeze multiple blank line' switch (default -S) is set to -s + character-code specifier (-s/-e/-j) are removed. use '-o kanjicode={S,E,J}' + instead. +Subject: [w3m-dev 01274] Re: SCM_NNTP + nntp: support. +Subject: [w3m-dev 01276] URL in w3m -v + when LANG=EN (or #undef JP_CHARSET), the URL displayed at w3m -v + (visual startup mode) is incorrect. + +2000/10/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + location of mailcap and mime.type can be specified in the Option Setting + Panel. + +2000/10/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01247] Re: buffer selection menu + Menu related patches. + ([w3m-dev 01227], [w3m-dev 01228],[w3m-dev 01229], [w3m-dev 01237], + [w3m-dev 01238]) + +2000/10/24 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * in the cookie-related setting, '.' is regarded as any domain. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01240] Re: w3m-0.1.11-pre-kokb17 patch + 'incompatible pointer type' fix. + +2000/10/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * "Domains from which should accept/reject cookies" setting in + the option setting panel. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01211] Re: a small change to linein.c +Subject: [w3m-dev 01214] Re: a small change to linein.c + * When editing long string, a part of the string disappear. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01216] error message for invalid keymap +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01220] Re: error message for invalid keymap + * w3m will display an error-message against the illegal + keymap file specification. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01217] keymap.lynx example could be better. + keymap.lynx update. + +2000/10/20 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + cookie-related improvements. + * version 1 cookie handling is now compliant to + http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt + Cookie2 is added in the Netscape-style cookie request header. + +2000/10/19 + +From: "Ambrose Li [EDP]" <acli@mingpaoxpress.com> +Subject: [w3m-dev-en 00136] version 0 cookies and some odds and ends +Subject: [w3m-dev-en 00191] sorry, the last patch was not made properly +Subject: [w3m-dev-en 00190] w3m-0.1.10 patch (mostly version 0 cookie handling) + I've hacked up a big mess (patch) against w3m-0.1.9 primarily + involving version 0 cookies. To my dismay, it seems that most + servers out there still want version 0 cookies and version 0 + cookie handling behaviour, and w3m's cookie handling is too + strict for version 0, causing some sites (notably my.yahoo.co.jp) + not to work. + +2000/10/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * pixel-per-character is now changable. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01208] '#', '?' in ftp:/.... + * w3m fails to parse URL when ftp:/ URL contains '#'. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01209] http_response_code and ``Location:'' header + w3m now follows Location: header only when + http_response_code is 301 - 303. + + +2000/10/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + local CGI makes zombie processes. + + +2000/10/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + w3m hangs when <textarea> is not closed in a table. + +From: maeda@tokyo.pm.org +Subject: [w3m-dev 00990] auth password input + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01193] Re: frame bug? + w3m eventually crashes when browsing frame pages. + +2000/10/13 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00928] misdetection of IPv6 support on CYGWIN 1.1.2 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01170] Re: cursor position after RELOAD, EDIT + * Bugfix: remove cache files + * The following functions can take arguments in keymap. + LOAD ... a file name + EXTERN, EXTERN_LINK ... a name of the external browser + (Can't be used from w3m-control: ) + EXEC_SHELL, READ_SHELL, PIPE_SHELL ... shell command + (Can't be used from w3m-control: ) + SAVE, SAVE_IMAGE, SAVE_LINK, SAVE_SCREEN ... a filename (or command name) + (Can't be used from w3m-control: ) + + +2000/10/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * the buffer name of an input taken from the stdin is now determined + from MAN_PN. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01156] Re: w3m-0.1.11-pre-kokb15 + * mydirname bugfix. + * SERVER_NAME can be configured. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01158] some bugs fix when RELOAD, EDIT +Subject: [w3m-dev 01164] cursor position after RELOAD, EDIT + * a file: called as local CGI can be edited. + +2000/10/10 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01166] Re: cell width in table + table-related bugfix. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01155] history of data for <input type=text> + <input type=text> input will be put into history buffer. + +2000/10/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01150] Some bug fixes + Bugfixes of the problems reported in + [w3m-dev 00956] unknown scheme in frame + [w3m-dev 00975] goto link from frame page + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01145] buffer overflow in linein.c + Fix of the buffer overrun problem in inputLineHist(linein.c) + +2000/10/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01136] function argument in keymap +Subject: [w3m-dev 01139] Re: function argument in keymap + Some functions specified in ~/.w3m/keymap can take an argument. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01143] image map with popup menu + image map can be treated as popup menu + (#define MENU_MAP in config.h) + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 00971] Re: segmentation fault with http: + Specifying 'http:' or 'http:/' as URLs will crash w3m. + +2000/10/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01134] w3m in xterm horribly confused by Japanese in title (fr + w3m-en will crash when browsing a page with Japanese title. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01127] SIGINT signal in ftp session (Re: my w3m support page) + SIGINT will crash w3m when downloading via ftp. + +2000/10/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * the maximum value of wmin in recalc_width() is changed to 0.05. + * when deflating compressed data other than http file and local file, + the file will be stored as a temporary file. + * mailcap edit= attribute support. + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Improvements of -dump, -source_dump options. + * Ignore <meta> tags in a frame. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00930] HTML-quote in w3mbookmark.c + * In 'Bookmark registration', URL and Title are not HTML-quoted. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00972] better display of progress bar ? + * An improvement of progress bar. + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Null pointer chech for textlist. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01100] space in URL + + * http://bugs.debian.org/60825 and http://bugs.debian.org/67466 + when submitting a form, name is not quoted (which should be) + + * http://bugs.debian.org/66887 + Remove preceding and trailing spaces from URL input through + 'U' command. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01113] bug fix (content charset) + content charset bugfix + + +2000/10/02 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01112] Re: mailcap test= directive + improvements of mailcap handling + * In addition to %s, %t (content-type name) will be available. + * nametemplate option is now valid. + * If there is no %s in the entry, 'command < %s' is assumed. + * If needsterminal is specified, spawn the command foreground. + * If copiousoutput is specified, load the command output into + buffer. + * `htmloutput' option is added, which indicates that the output + of the command is to be read by w3m as text/html. For example, + + application/excel; xlHtml %s | lv -Iu8 -Oej; htmloutput + + enables an Excel book to be displayed as an HTML document. + * compressed file browsing support for ftp scheme. + * Bug: compressed file isn't displayed properly for http: scheme. + +2000/09/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01097] gunzip_stream problem + * Fix of the behaviour against INT signal while reading compressed + file. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01092] CONFIG_FILE + * CONFIG_FILE in config.h was hard-coded. + +2000/09/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01078] treatment of content type + Improvements around document type handling. + * precedence between lessopen_stream() and gunzip_stream() was changed + in examineFile(). + * lessopen_stream()ed file is treated as a plein text. + * lessopen_stream() is applied only if document type is text/* or + no external viewer is set. + * all text/* data other than text/html are handled inside w3m. + * The document type displayed by page_info_panel() is now the one + before examineFile() processing. + * When invoking an external viewer, ">/dev/null 2>&1 &" is appended + to the command line. + +2000/09/13 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01053] Re: Location: in local cgi. + * Do not interpret Location: header of the local file when invoking + with -m flag, + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01065] map key '0' + Improvement around keymap. + * Now a single '0' can be mapped. Numbers other than 0, for example + `10 j' are regarded as prefix arguments. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01066] 104japan + * Code conversion fix for forms in frame. + +2000/09/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01058] <dt>, <dd>, <blockquote> (Re: <ol> etc.) + * insert blank lines before and after <blockquote>. + * Don't ignore <p> tag just after <dt> and <dd>. + +2000/09/04 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01052] cellpadding, cellspacing, vspace, etc. + Some changes about space character and blank lines: + * <a name="..."></a> or <font> outside <tr> or <td> are pushed + into the next cell. + * cellspacing attribute in <table> tag is now handled correctly. + * vspace attribute interpretation. + * blank line detection criterion is changed. + * </p> tag inserts a blank line. + +2000/08/17 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01018] sqrt DOMAIN error in table.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01019] Re: sqrt DOMAIN error in table.c + * fix of DOMAIN error of sqrt(). + +2000/08/15 + +From: satodai@dog.intcul.tohoku.ac.jp (Dai Sato) +Subject: [w3m-dev 01017] value of input tag in option panel + * Fix of the problem of the option setting panel: when specifying + a value including a double quote, the value after " is not displayed + on the next invocation of the option setting panel. + +2000/08/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01016] Table geometry calculation + * rounding algorithm of table geometry calculation is changed to + minimize the difference of the column width and the `true' width. + +2000/07/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01006] initialize PRNG of openssl 0.9.5 or later + * when using openssl library after 0.9.5, enables SSL on the environment + without the random device (/dev/urandom). + +2000/07/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01004] unused socket is not closed. + When interrupting file transfer using C-c, the socket sometimes + stay unclosed. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01005] table caption problem + Fix of the problem that w3m doesn't stop when there's no closing + </caption>. + +2000/07/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00966] ssl and proxy authorization + Fix of the authorization procedure of SSL tunneling via HTP proxy. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01003] Some bug fixes for table + +2000/07/16 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00999] Re: bookmark + * Sometimes a link can't be appended into the bookmark. + +2000/06/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00934] clear_buffer bug + Bugfix: when clear_buffer is TRUE, selBuf() clears the screen. + +2000/06/17 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00929] ftp.c patch + Return code 230 against USER command is regarded as a successful + login. + +2000/06/16 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00923] some bug fixes + * when #undef JP_CHARSET, file.c doesn't compile. + * buffer.c bugfix ("=" should be "==") + +From: Kazuhiko Izawa <izawa@nucef.tokai.jaeri.go.jp> +Subject: [w3m-dev 00924] Re: w3m-0.1.11pre +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00925] Re: w3m-0.1.11pre + Accessing URL like file://localhost/foo causes abnormal termination. + +2000.6.6 +From: aito +* [w3m-dev 00826] + * Bugfix: When a header by CGI POST method gives Location: header, + the redirect can't be reloaded. + * white spaces in URL are removed. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00827] Bugfix: onA() doesn't work. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00835] Improvement of 'Jump to label' behavior within a frame. + +2000.6.5 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00789] Bugfix: width of <li> +* [w3m-dev 00801] Bugfix: Line break occurs on . +* [w3m-dev 00813] Bugfix: single > in a document isn't analyzed properly. +* [w3m-dev 00818][w3m-dev 00819] Bugfix: <xmp>,<listing> in <textarea> +* [w3m-dev 00820] Screen shift bugfix. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00807] Bugfix: <option> without <select> in table causes core dump. +* [w3m-dev 00816] Bugfix: CRs in <textarea>..</textarea> are replaced with + white spaces. + +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00814] Bugfix: After specifying non-text file in 'V' command, + w3m dumps core. + +2000.6.1 +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00581] BUFINFO related bugfix. +* [w3m-dev 00641] Bugfix: extbrowser setting in config desn't work. +* [w3m-dev 00660] Bugfix: pathname to invoke external viewer becomes like + ``/home/okada/.w3m//w3mv6244-0..pdf''. +* [w3m-dev 00701] enhancement of [w3m-dev 00684]. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00582] Bugfix: kterm mouse, etc. +* [w3m-dev 00586] Bugfix; when CLEAR_BUF is defined, buffer size is displayed as [0 line]. +* [w3m-dev 00605] + * show_params() improvement. + * when CLEAR_BUF is defined and reloading local file, that is overwritten. +* [w3m-dev 00606] When submitting data in textarea without editing them, CR charcters are + sent instead of CRLF. +* [w3m-dev 00630] Bugfix of mouse-dragging behaviour. +* [w3m-dev 00654] [w3m-dev 00666] When CLEAR_BUF is defined, content of form disappears. +* [w3m-dev 00677] [w3m-dev 00704] Improvement of Japanese coding-system decition algorithm. +* [w3m-dev 00684] Command line analysis enhancement. +* [w3m-dev 00696] Bugfix of PIPE_SHELL('#'), READ_SHELL('@') and EXEC_SHELL('!'). +* [w3m-dev 00706] Bugfix: When CLEAR_BUF is defined, anchors created by : disappears. +* [w3m-dev 00720] Enhancement of dirlist.cgi. +* [w3m-dev 00724] when -m option is used, continuation lines in header are not + processed properly. +* [w3m-dev 00728] handling of Japanese character in HTTP header. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00589] Bugfix: w3m dumps core after like w3m -T a -B and save command. +* [w3m-dev 00595][w3m-dev 00610] frameset related bugfix. +* [w3m-dev 00631][w3m-dev 00633] ID_EXT related bugfix. +* [w3m-dev 00632] Bugfix? handling of character-entity (") in attribute. +* [w3m-dev 00646] Enhancement: frame names are embedded as id attribute in + the frame-table. +* [w3m-dev 00680] +* [w3m-dev 00683] Bugfix: <STRONG> tags become comments in frame. +* [w3m-dev 00707] frame related bugfix. +* [w3m-dev 00774] Bugfix: as some file descriptors are not closed, file descriptors + are exhausted on a certain condition. + +From: SASAKI Takeshi <sasaki@sysrap.cs.fujitsu.co.jp> +* [w3m-dev 00598] ID_EXT related bugfix. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00602] Bugfix: a blank line is inserted when processing <title>...</title>. +* [w3m-dev 00617] <table> within <blockquote> in <table> corrupts. +* [w3m-dev 00675] Support of terminals which can't display (0xa0). +* [w3m-dev 00732] <!--comment --\n> like comment handling fix. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +* [w3m-dev 00679] USE_SSL_VERIFY fix. +* [w3m-dev 00686] w3mhelperpanel.c fix. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +* [w3m-dev 00692] EWS4800 support for /usr/abiccs/bin/cc. + +From: Hiroshi Kawashima <kei@arch.sony.co.jp> +* [w3m-dev 00742] mipsel architecture support. + +2000.5.17 +From: Hiroaki Shimotsu <shim@d5.bs1.fc.nec.co.jp> +* [w3m-dev 00543] Bugfix: personal_document_root doesn't work. +* [w3m-dev 00544] When opening file:///dir/, if index.html exists in + that directory, open the file instead of displaying directory list. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00545] w3m -num fix. +* [w3m-dev 00557] Bugfix: When using -dump option, temporary files don't be unlinked. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00568] Bugfix: When <blockquote> exists outside of <tr>..</tr> in <table>, + the table corrupts. + +2000.5.16 +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00487] Bugfix: supports terminal without sr capability. +* [w3m-dev 00512][w3m-dev 00514][w3m-dev 00515] Kanji-code decision enhancement. +* [w3m-dev 00530] Bugfix: w3m can't handle cgi using <ISINDEX>. +* [w3m-dev 00537] Remove CR/LF within URL. +* [w3m-dev 00542] Layered frameset support. + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +* [w3m-dev 00488] id attribute support bugfix. +* [w3m-dev 00497] configure automatically detects IPv6 environment. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +* [w3m-dev 00489] + * Bugfix: a page doesn't be displayed which requires SSL client authentication. + * Enhancement: -o option parameter list + * [w3m-dev 00519] Security hole fix about I command. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00498] Filename completion bugfix. +* [w3m-dev 00508] Color display bugfix. +* [w3m-dev 00518] Security hole fix about I command. +* [w3m-dev 00535] GPM/SYSMOUSE support bugfix. + +From: Kazuhiro Nishiyama <nishiyama@mx1.tiki.ne.jp> +* [w3m-dev 00503] $extension correction in Cygwin. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +* [w3m-dev 00516] When transmitting a string to form, it was made not to escape + a safe character. + +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00471] Bugfix: when displaying the page which has a link in the + beginning of the screen, the first link doesn't get active. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +* [w3m-dev 00539] proxy initialization bugfix. + +2000.4.24 +From: aito +* free memory of hidden buffers. (CLEAR_BUF) +* when file:// style local file access fails, don't retry + as http://file://. +* Bugfix: mouse doesn't work when both GPM and SYSMOUSE are undefined. + +From: rubikitch <rubikitch@ruby-lang.org> +* Enhancement: Save Buffer URL into file. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* FTP proxy bugfix. +* C comment cleanup. +* Bugfix: After window resize, reshapeBuffer() is called on + each keyin. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +* when gc library exists under /usr/local, configure decides + found -> dones't seem to work. + +2000.4.21 +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +* Enhancement: When FTP login password ends with @, append the FQDN of + the host. +* When environment variable NNTPMODE is defined, send MODE command + using the value. +* Following options are added. + ssl_verify_server ON/OFF + Do SSL server verification (default OFF) + ssl_cert_file filename + PEM certification file for SSL client (default NULL) + ssl_key_file filename + PEM secret key file for SSL client (default NULL) + ssl_ca_path directory + Path for the directory of PEM certification files (default NULL) + ssl_ca_file file + Filename of PEM certification files + +From: Tsutomu Okada <okada@furuno.co.jp> +* Bugfix: DEL key causes core dump in line input mode. +* Comment processing bugfix. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* Proxy authentication support for FTP proxy. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* Bugfix: <input_alt fid=0> causes core dump. + +From: aito +* Bugfix: When a table is in a cell with more than one colspan, + the width of the inner table gets wired. +* -model=, -lang= options are added to configure. + +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- All T/NILs are replaced with TRUE/FALSE. +- Messages are added for FTP connection. + +2000.4.7 +From: aito +* Bugfix: <select> without </select> causes core dump. +* Bugfix: Compilation fails unless MOUSE is defined. + +From: Tsutomu Okada <okada@furuno.co.jp> +* Bugfix: Following a link to a local file causes core dump. +* Bugfix: DEL key in line editing causes core dump. + +From: Shin HATTORI <mituzi@he.mirai.ne.jp> +* Bugfix: bzip2 support. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* Bugfix: -dump, -dump_head, -dump_source option interfares + each other. +* Improvement: -o option added. +* Bugfix: -dump option causes core dump. +* Bugfix: mouse operation gets inactive while message is displayed. +* Bugfix: window size change. +* Improvement: Default of quit confirmation is now 'n' +* term.c enhancements + +From: Sven Mascheck <mascheck@faw.uni-ulm.de> +* There are websites using (unprintable) special characters (eg '0x96') + to 'feature' microsoft browsers. At least in the western configuration + (the only one i know), w3m doesn't check if characters are printable, + thus they confuse particularly the /xfree/ xterm (knowing more special + characters than other xterms). + Something like the attached patch could prevent this + (also only affects western version). + Instead of (superfluously) using isprint() with the locale, + it now just checks the range (pointed out by Christian Weisgerber). + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +* C++ style comments are changed into C style. + +2000.4.6 +From: lars brinkhoff <lars@nocrew.org> +ARM linux patch. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Improvement: 'u' command shows form type and action. + +From: patakuti +* Bugfix: -dump option for other than text/html document doesn't work. +* Improvement: Association between extension and mime-type can be + specified by ~/.mime.types. + +2000.4.5 +From: Sakamoto <hsaka@mth.biglobe.ne.jp> +* Bugfix: <Hn>...</Hn> in table makes the table width weird. +* Bugfix: </ol>,</ul>,</dl>,</blockquote> without opening tag + make the table ugly. + +From: "Shin'ya Kumabuchi" <kumabu@t3.rim.or.jp> +* Bugfix: w3m eventually sends Pragma: no-cache header inappropriately. + +From: Tomoyuki Kosimizu <greentea@fa2.so-net.ne.jp> +Bugfix around rc.c + +2000.3.29 +From: Altair <NBG01720@nifty.ne.jp> +OS/2 support improvement. +* Bugfix: w3m fails to open directory. +* Improvement: arrow keys are effective in non-X environment of OS/2 +* Bugfix: Couldn't invoke external program. +* Improvement: Enable drive letter. + +From: David Leonard <leonard@csee.uq.edu.au> +after filling in a simple form + <form action="https://internal.csee.uq.edu.au/cgi-bin/login.cgi" method=POST> +a cookie is received and then w3m dumps core. + +From: Ken Yap <ken@nlc.net.au> +I have made w3m work on DJGPP (protected mode 32-bit programs running +from DOS/Win). The resulting binary after compression is only 220kB, +which means it's possible to give a floppy for a 386 with 4-8 MB memory +for browsing the web! + +From: "SHIROYAMA Takayuki" <psi@stellar.co.jp> +From: Jeroen Scheerder <J.Scheerder@cwi.nl> +MacOS X Server patch. + +2000.2.25 +From: Ambrose Li +I found a bug in <img alt=""> +handling. If alt="" is not suppressed, the line containing the img +element is not wrapped. I have verified that the bug exists in w3m +0.1.6; the bug seems to still exist in w3m 0.1.7, but I have not +finished compiling it. + +From: aito +Bug fix: <option> without <select> causes core dump. +Bugfix: The first line in <blockquote> doesn't indented. +Improvement: application/x-bzip support. +Bugfix: GC fails in mktable, w3mbookmark, w3mhelperpanel. +Bugfix: mouse drag. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bug fix: Illegal tags make w3m's behavior unstable. +quoteShell() security hole fix. +Bug fix: w3m dumps core inside set_environ(). +Bug fix: w3m doesn't do <table width="xxx%"> +Improvement: '!' command doesn't make screen dirty. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Bug fix: Temporary file paths contain //. +Bug fix: 0.1.7 fails with https. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Changes w3m's behavior such that connection failure to the proxy +server causes an error. +Bug fix: URLs specified in the command line don't be involved +into URL history. + +From: sasaki@ct.sakura.ne.jp +HTML4.0 ID attribute support. + +From: Okabe Katsuya <okabe@fphy.hep.okayama-u.ac.jp> +table get weird when it contains <input type=hidden>. +$B=$@5!%(B + +2000.2.12 +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- added GNU-style comments for all #ifdef/#else/#endif + modified: almost all files +- renamed w3mhelp_en and w3mhelp_ja to w3mhelp-w3m_en and w3mhelp-w3m_ja + (to aid in handling of additional keybindings in the future) + modified: XMakefile, XMakefile.dist, config.h, configure, help files +- corrected error in w3mhelp-lynx_en ('Japanese' link was pointing to + Japanese language help file for the w3m keybinding, not the lynx + keybinding) + modified: w3mhelp-lynx_en.html +- replaced 'Loading {URL}' message with more specific messages about + current status ('Performing hostname lookup on {hostname}' and + 'Connecting to {hostname}') + modified: main.c, url.c + +2000.2.10 +From: roguemtl@stampede.org (Jacob Moorman of the [MH] Free Software Group) +- added support for PageUp and PageDown in list boxes + +2000.1.21 +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +1. conn.eventMask is set to 0 which disables reception of all types + of events. Effectively, this disables GPM support altogether. + Probably "~0" was intended, to enable reception of all types of + events. +2. conn.maxMod is set to ~0, which means that events with a modifier + key (shift, control, etc.) set are also sent to w3m. Since w3m + doesn't do anything with these events, they should rather be + passed on to other clients. Changing this to "conn.maxMod = 0" + will for example allow the use of the mouse in w3m *and* mouse + clicks with shift held down for console cut-and-paste. + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +I would like to suggest a small change to w3m's GPM support: +Rather than explicitly drawing the mouse pointer, this could be left to +the server, i.e. +- remove GPM_DRAWPOINTER() calls, +- set conn.defaultMask to GPM_MOVE|GPM_HARD. + +From: aito +When '<' is used as other than start of a tag, let the '<' +be displayed. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Bug fix of screen redraw. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +* Accept discard attribute of Set-Cookie2. +* insert a blank line just after </dl>. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +<table> Geometry calculation bugfix. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bugfix of inputLineHist(). + +2000.1.14 +From: ChiDeok Hwang <cdhwang@sr.hei.co.kr> +When I browse http://i.am/orangeland and press 'v' to see document +info, w3m got seg. fault. +Reason was above site had the very strange frameset with only one frame. +<frameset rows="100%,*" ... > +Simple following fix was enough for me. + +From: aito +When no scheme is specified in the URL, w3m tries to open local file, +and when it fails w3m prepends "http://". + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +target="_parent" support +frame-relatex bugfixes. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Screen redraw bugfix. + +2000.1.12 +From: aito +word fill support. (undocumented) +add #define FORMAT_NICE in config.h. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +"w3m ." and w3mhelperpanel doesn't work. + +2000.1.11 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Bugfix of cookie behavior. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +table geometry calculation improvement. + +From: aito +C-c make the external viewer to exit. + +From: <sekita-n@hera.im.uec.ac.jp> +Added an option to suppress sending Referer: field. + +2000.1.4 +From: Sven Oliver Moll <smol0999@rz.uni-hildesheim.de> +There was one thing that's been anoying me, so I got it fixed: the +behaviour of mouse dragging. The push of the mousebutton is +interpreted of dragging the text behind the window. My intuition in +dragging is that I drag the window over the text. So I added a config +option called 'reverse mouse'. + +From: aito +'M' command (external browser) added to Lynx-like keymap. + +From: SUMIKAWA Munechika <sumikawa@ebina.hitachi.co.jp> +IPv6 related bugfix. + +From: kjm@rins.ryukoku.ac.jp (KOJIMA Hajime) +NEWS-OS 6.x support. + +From: aito +configure detects IPv6 support automatically. +(Thanks to sumikawa@ebina.hitachi.co.jp) + +1999.12.28 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of link coloring/underlining. +From: Fumitoshi UKAI <ukai@debian.or.jp> +Now w3m has an option not to render <IMG ALT="">. + +From: aito +Even when HTTP server response code is other than 200, +w3m does user authorization if WWW-Authenticate: header exists. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +When following the Location: header, w3m doesn't chop the +LF character off. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Improvements of table rendering algorithm. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Now w3m allows a comment <!-- .... -- > (spaces between -- and >) + +1999.12.27 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Improvement of dirlist.cgi. + +1999.12.14 +From: Christian Weisgerber <naddy@unix-ag.uni-kl.de> +- I have appended a small patch to add support for the Home/End/ + PgUp/PgDn keys at the FreeBSD "syscons" console. + (It would be much preferable if w3m read the key sequences from + the termcap entry rather than having them hardcoded, but this + would require a substantial rewrite.) + +From: aito +* w3m-control: GOTO url support. +* When a document has <meta http-equiv="Refresh" content="0; url=URL"> + tag, the moved document is loaded immediately. +* When invoking an external browser by 'M' or 'ESC M' and the browser + is not defined, w3m prompts to input command to process the URL. + +1999.12.8 +From: aito +Proxy-Authorization support. + +1999.12.3 +From:aito +Now w3m can use an external command (local CGI script) for +directory listing. Default is hsaka's dirlist.cgi. +(in `scripts' directory) + +1999.12.2 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +In menu selection and buffer selection mode, cursor now points +the selected item. (for blind people's convenience) + +From: aito +Now w3m doesn't use GPM library when using +xterm. + +1999.12.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Starting up with environment variable HTTP_HOME +causes hangup. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Some kind of form causes segmentation fault. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +align attribute support for <tr> tag. +Now default alignment of <th> become CENTER. + +From: Tsutomu Okada <okada@furuno.co.jp> +COOKIES in func.c is changed to COOKIE + +From: aito +Now w3m accepts HTTP headers which have no white space after :. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Serial number of anchors in TABLE gets incorrect. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of -v option. + +1999.11.26 +From: Fumitoshi UKAI <ukai@debian.or.jp> +When arguments in an external command in mailcap +are enclosed by ' ', the command is not executed +correctly. + +1999.11.20 +From: SASAKI Takeshi <sasaki@isoternet.org> +Turning 'Active link color' on causes core dump. + +1999.11.19 +From: aito +Now w3m uses GPM library if available. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Further enhancement of progress bar. + +1999.11.18 +From: Ben Winslow <rain@insane.loonybin.net> +Enhancement of progress bar. + +From: patakuti +If an <input type=button> tag has no `name' attribute, +w3m adds it an inappropriate name attribute. + +From: $B$d$^(B +Now w3m can handle a frameset that has both ROWS and COLS. + +From: aito +Now bookmarking is done by a separate command w3mbookmark. + +C-s $B$G2hLLI=<($,;_$^$C$F$$$?%P%0$N=$@5!%(B + +$BJ8;zF~NO;~$K(B C-g $B$GCf;_$G$-$k$h$&$K$7$?!%(B + +From: hovav@cs.stanford.edu +When downloading a file, an attempt to save it to a non-exist +directory causes core dump. + +From: minoura@netbsd.org +A character entity like ሴ (greater than 0xff) +causes segmentation fault. + +From: Christi Alice Scarborough <christi@chiark.greenend.org.uk> +Active link color patch. + +1999.11.17 +From: aito +Now <OL>,<UL> make blank line before and after the list only if +the list is in the first level. +A bookmark file can be specified by -bookmark option. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Now 'N' is bound to 'search previous match'. + +1999.11.16 +From: Kiyohiro Kobayashi <k-kobaya@mxh.mesh.ne.jp> +Enhancement of FTP directory listing. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +checkContentType() Bug fix + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Menu behavior is changed. +* C-f,C-v : display next page +* C-b,M-v : display previous page +* ^[[L (console), ^[[E (pocketBSD) are recognized as INS key. +* DEL : back to parent menu (same as C-h) +* #define MENU_THIN_FRAME -> use thin rules to draw menus + (default is #undef) +* Now one can move to next/previous menu page by clicking + ':' on the bottom/top of the menu. +* Clicking outside the menu causes cancellation of sub-menu. +* <, >, +, - abandoned + +From: $B$*$+$@(B <okada@furuno.co.jp> +Now C-a/C-e are bound to 'jump to the first/last item in menu.' + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +From: Fumitoshi UKAI <ukai@debian.or.jp> +w3m doesn't recognize FTP Multiline reply. + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +Bugfix of buffer selection mode. + +1999.11.15 +From: aito +If a document has <BASE> tag and the base URL is different +from the current URL, w3m sends incorrect Referer: value. +A control character written as &#xnnn; (for example, 
) +is not be decoded correctly. +Now local-CGI scripts have to be located in either file:///cgi-bin/ +or file:///usr/local/lib/w3m/. Scripts in any other directory +are treated as plain texts. +Most system() calls are replaced with mySystem(). + +1999.11.11 +From: aito +Tagname search algorithm is changed from linear search to +hash table. + +1999.11.5 +From: aito +If a table contains character entities &xxx; which represent +latin-1 characters, column width gets incorrect. + +1999.11.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +w3m-991028+patch1 doesn't compile when compiled without menu. + +From: ukai@debian.or.jp +A bugfix of Strcat_char(). + +1999.10.28 +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +When accessing + file?var=value/#label +#label doesn't be regarded as a label. + +From: aito +991027 version contains debug code. + +1999.10.27 +From: OKADA <okada@furuno.co.jp> +When JP_CHARSET is defined, Latin-1 character like ¢ isn't +displayed. + +From: Yama +A cookie without path haven't been handled correctly. + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +When reloadint CGI page with POST method, w3m tries to load it +with GET method. + +From: aito +When following a link from a page in frame, Referer: value +is not a URL of the page but the URL of fremeset page. + +configure is slightly changed. User can now choose model. +-yes option added. + +FTP failed when a server returns other than 150 as a result code +of RETR/NLST command. + +<select multiple>...</select> doesn't work correctly. + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +In getshell, getpipe, execdict functions, +buffer name is changed to include command name. + +From: Colin Phipps <cph@crp22.trin.cam.ac.uk> +When a load of cookies expire w3m SEGVs on startup. + +From: pmaydell@chiark.greenend.org.uk +I was looking through the w3m source, and noticed that it defines the +following macro: +#define IS_SPACE(x) (isspace(x) && !(x&0x80)) +Now this won't work as expected if x is an expression with side effects +(it will be evaluated twice). A quick grep of the sources reveals +several places where the macro is used like this: +file.c: if (IS_SPACE(*++p)) +which is almost certainly a bug... (although I haven't tried to actually +work out what the effects of it would be). + +1999.10.21 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of buffername of source/HTML display. + +1999.10.20 + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +When <P> exists between <dt> and <dd> or <h3> and </h3>, +all text after that point becomd bold. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Now 'B' command backs to previous page when viewing frame page. +'F','v','=' command toggle. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Inappropriate behaviours with -dump option have been fixed. +* w3m -dump < file appends \377 before EOF. +* w3m -dump -s < file doesn't convert character code. + -num, -S don't take effect. +* w3m -dump -T text/plain < file outputs nothing. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* menu.c: graphic char related bugfixes. +* terms.c: When Cygwin, T_as = T_as = T_ac = "" + Added T_ac != '\0' in graph_ok() +* bookmark.c: KANJI_SYMBOL -> LANG == JA + +1999.10.15 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> + 1. Some part does case-sensitive comparison for cookie name. + 2. When executing sleep_till_anykey(), terminal state doesn't + recover. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +When running configure, it adds multiple -lxxx when +both libxxx.a and libxxx.so exist. [fixed] + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* When generating HTML within w3m, those source didn't be quoted. +* Makes it interpret <base href=... target=...> of each source + of a frame. + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Enhancements of buffer handling functions. + +From: SASAKI Takeshi <sasaki@isoternet.org> +When multiple cookies are sent whose names are different +but domains and paths are identical, older one doesn't +removed. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Cookie comparison must be case insensitive. + +From: aito +* When no ~/.w3m/cookie exists, C-k sometimes cause core dump. +* ~/.w3m/cookie isn't updated when -dump option is used. +* Latin-1 character written as &xxx; isn't regarded as a + roman character. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Improvements: + o After executing "w3m http://foo.co.jp/foo.jpg", + w3m waits with prompt "Hit any key to quit w3m:". + o "w3m http://foo.co.jp/foo.tar.gz" doesn't display + usage. + o Progress bar is displayed while downloading via ftp. + o FTP download can be interrupted. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Now w3m can access using SSL via proxy server. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +<form enctype="multipart/form-data"> <input type=file> + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +w3m-991008 on cygwin causes following problems. + 1. w3m / doesn't display directory list. + 2. When referring local directory, URL becomes like + file://///foo. + 3. Can't load file:///windows. + +From: Fumitoshi UKAI <ukai@debian.or.jp> + % http_proxy=http://foo/bar w3m http: +causes segmentation fault. + +1999.10.8 +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Changed to treat documents that contain designation sequences +to JIS-X0208-1983 and JIS-X0208-1976. + +From: aito +When there is tag sequence like <pre>..<p>..</pre><p> , words +after the last <p> are not folded. [fixed] +Type of counters for number of anchor in a document are changed from +short to int. +Description like `<b><u>hoge</u></b> moge' makes the space between hoge +and mode underlined.[fixed] + +1999.10.7 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Cookie support. +SSL support. Still experimental. + +From: aito +Considering those systems who have no static library, now configure +searches lib*.so as well as lib*.a. + +From: HIROSE Masaaki <hirose31@t3.rim.or.jp> +From: Anthony Baxter <anthony@ekorp.com> +`Host:' header lacks port number. [fixed] + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +When moving to a label, URL doesn't change. [fixed] + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Can't handle tags like <ul> nested more than 20 levels.[fixed] + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bugfix about Content-Transfer-Encoding: quoted-printable + +From:aito +When <frameset > contains both COLS= and ROWS=, w3m can't render +that frame correctly. [fixed] + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Bugfixes of w3m-990928 gziped file support. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +* Bug fix of decoding B-encoded file. + +1999.9.28 + +From: SASAKI Takeshi <sasaki@isoternet.org> +wrap search mode is added. +&#xnnnn; character entity representation support. + +From: aito +Change default character color to 'terminal' (do nothing). + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +When using BG_COLOR defined w3m on linux console, w3m doesn't refresh +screen on termination. [fixed] + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Extra newline is inserted after <pre> tag in frame. [fixed] + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +When opening popup menu by mouse right click, let cursor move to +the clicked position. + +1999.9.16 + +From: aito +Fix a bug that renders <...> in form button as <...> tag. + +From: Iimura uirou@din.or.jp +w3m causes SIGSEGV when DICT is defined in config.h. [fixed] +Added a function to make a link to the background image. + +From: Doug Kaufman <dkaufman@rahul.net> +I just downloaded and compiled the 19990902 version of w3m with cygwin +(b20 with 19990115 cygwin1.dll). The following patch takes care of the +compilation problems. + +From: Hayase +There are undefined variables when compiled on NEXTSTEP 3.3J. [fixed] + +From: Oki +When using <HR> tag in list environment, let horizontal rule begin +from indented position. + +From: Okada +w3m gets segmentation fault when rendering character entity like ア. +[fixed] + +From: Sakamoto hsaka@mth.biglobe.ne.jp +Many bugfixes for local CGI. + +From: Katsuya Okabe okabe@okaibm.hep.okayama-u.ac.jp +Graphics characters go bad when using on linux console. [fixed] +Bug fixes on color display on kterm. [fixed] +Bug fix on table renderer. [fixed] + +From: Sakamoto hsaka@mth.biglobe.ne.jp +Download from page in frame doesn't work correctly. [fixed] + +1999.9.3 +From: Sakamoto hsaka@mth.biglobe.ne.jp +Bug fixes of URL analyzer. + +From: Katsuya Okabe okabe@okaibm.hep.okayama-u.ac.jp +Bugfix of screen redraw routine. +-- +Akinori Ito +tel&fax: 0238-26-3369 +E-mail: aito@eie.yz.yamagata-u.ac.jp + diff --git a/doc/MANUAL.html b/doc/MANUAL.html new file mode 100644 index 0000000..724340e --- /dev/null +++ b/doc/MANUAL.html @@ -0,0 +1,504 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head><title>w3m manual</title> +</head> +<body> +<h1>w3m MANUAL</h1> +<div align=right> +Akinori Ito<br> +aito@ei5sun.yz.yamagata-u.ac.jp +</div> +<h2>Index</h2> +<menu> +<li><a href="#Introduction">Introduction</a> +<li><a href="#Options">Options</a> +<li><a href="#Color">Document color</a> +<li><a href="#Key:orig">Key binding</a> +<li><a href="#Key:lynx">Lynx-like key binding</a> +<li><a href="#Mouse">Mouse operation</a> +<li><a href="#Key:custom">Key customization</a> +<li><a href="#LocalCGI">Local CGI</a> +</menu> + +<hr> +<a name="Introduction"></a> +<h2>Introduction</h2> +w3m is a pager/text-based WWW browser. You can browse local documents and/or +documents on the WWW using a terminal emulator. + +<hr> +<a name="Options"></a> +<h2>Options</h2> + +Command line usage is +<p> +<pre> + w3m [options] [file|URL] +</pre> +<P> +If you specify filenames/URLs on command line, these documents are displayed. +If you specify nothing, w3m reads a document from standard input and display it. +If no filename and/or URLs are specified and standard input is tty, w3m terminates +without displaying anything. + +<p> +Options are as follows: +<dl> +<dt>+<line number> +<dd>Move to the specified line. +<dt>-t width +<dd>Specify tab width. Default is 8. +<dt>-r +<dd>When displaying text/plain document, prohibit emphasis using backspace. +If you don't specify this option, +``A^H_'' is interpreted as underlined character and ``A^HA'' as a bold character. +<dt>-S +<dd>When displaying text/plain document, squeeze blank lines. +<dt>-l number +<dd>Specify line number preserved internally when reading text/plain document +fron standard input. Default is 10000. +<dt>-s +<dd>Display documents with Shift_JIS code. +<dt>-e +<dd>Display documents with EUC_JP code. +<dt>-j +<dd>Display documents with ISO-2022-JP code. +<dt>-T type +<dd>Specify document type. Without this option, document type +is determined from extension of a file. If the determination +fails, the document is regarded as text/plain. +<p> +Example:<br> +Read HTML document from standard input and display it +<pre> + cat hoge.html | w3m -T text/html +</pre> +<p> +Display HTML source +<pre> + w3m -T text/plain hoge.html +</pre> +<dt>-m +<dd>Display document with Internet message mode. +With this option, w3m determines document type from header infomation. +It is useful when reading E-mail or NetNews messages. +<dt>-B +<dd>Show the bookmark. +<dt>-bookmark file +<dd>Specify bookmark file. +<dt>-M +<dd>Monochrome display mode. +<dt>-F +<dd>Automatically render frame. +<dt>-S +<dd>Sqeeze blank lines. +<dt>-X +<dd>Upon exit, do not display preserved screen. +<dt>-W +<dd>Toggle wrap search mode. +<dt>-o option=value +<dd>Specify option. The option names and values are +same as that appears in ~/.w3m/config. +<dt>-cookie +<dd>Process cookies. +<dt>-no-cookie +<dd>Don't process cookies. +<dt>-pauth username:password +<dd>Set username and password for (basic) proxy authentication. +<dt>-num +<dd>Show linenumber. +<dt>-dump +<dd>Read document specified by URL and dump formatted text into standard +output. The width of the document become 80. This width can be overridden +with -cols option. +<dt>-cols width +<dd>Specify document width. Used with -dump option. +<dt>-ppc count +<dd> Specify the number of pixels per character (default 8.0). Larger + values will make tables narrower. +<dt>-dump_source +<dd>Read document specified by URL and dump the source. +<dt>-dump_head +<dd>Read document specified by URL and dump headers. +<dt>-no-proxy +<dd>Don't use proxy server. +<dt>-no-graph +<dd>Don't use graphic character to draw frames. +<dt>-no-mouse +<dd>Don't activate mouse. +</dl> + +<hr> +<a name="Color"></a> +<h2>Document color</h2> + +Links and images are displayed as follows. +<div align="center"> +<table border="1"> +<tr><th> </th><th>Color mode</th><th>Monochrome mode</th></tr> +<tr><td>links</td><td>blue</td><td>underline</td></tr> +<tr><td>inline images</td><td>green</td><td>reverse</td></tr> +<tr><td>form input</td><td>red</td><td>reverse</td></tr> +</table> +</div> +These colors can be customized using option setting command "o". + +<hr> +<a name="Key:orig"></a> +<h2>Key binding</h2> + +After invocation, you can operate w3m by one-character commands from +the keyboard. +<P> +Here's the original key-binding table. If you are using Lynx-like key +bindings, see <a href="#Key:lynx">the Lynx-like key binding</a>. + +<H3>Page/Cursor motion</H3> +<table> +<TR><TD WIDTH=100>SPC,C-v<TD>Forward page +<TR><TD>b,ESC v<TD>Backward page +<TR><TD>l,C-f<TD>Cursor right +<TR><TD>h,C-b<TD>Cursor left +<TR><TD>j,C-n<TD>Cursor down +<TR><TD>k,C-p<TD>Cursor up +<TR><TD>J<TD>Roll up one line +<TR><TD>K<TD>Roll down one line +<TR><TD>w<TD>Go to next word +<TR><TD>W<TD>Go to previous word +<TR><TD>><TD>Shift screen right +<TR><TD><<TD>Shift screen left +<TR><TD>.<TD>Shift screen one column right +<TR><TD>,<TD>Shift screen one column left +<TR><TD>g<TD>Go to the first line +<TR><TD>G<TD>Go to the last line +<TR><TD>ESC g<TD>Go to specified line +<TR><TD>TAB<TD>Move to next hyperlink +<TR><TD>ESC TAB<TD>Move to previous hyperlink +<TR><TD>[<TD>Move to the first hyperlink +<TR><TD>]<TD>Move to the last hyperlink +</table> + +<H3>Hyperlink operation</H3> +<table> +<TR><TD WIDTH=100>RET<TD>Follow hyperlink +<TR><TD>a, ESC RET<TD>Save link to file +<TR><TD>u<TD>Peek link URL +<TR><TD>I<TD>View inline image +<TR><TD>ESC I<TD>Save inline image to file +<TR><TD>:<TD>Mark URL-like strings as anchors +<TR><TD>ESC :<TD>Mark Message-ID-like strings as news anchors +<TR><TD>c<TD>Peek current URL +<TR><TD>=<TD>Display infomation about current document +<TR><TD>F<TD>Render frame +<TR><TD>M<TD>Browse current document using external browser +(use 2M and 3M to invoke second and third browser) +<TR><TD>ESC M<TD>Browse link using external browser +(use 2ESC M and 3ESC M to invoke second and third browser) +</table> + +<H3>File/Stream operation</H3> +<table> +<TR><TD WIDTH=100>U<TD>Open URL +<TR><TD>V<TD>View new file +<TR><TD>@<TD>Execute shell command and load +<TR><TD>#<TD>Execute shell command and browse +</table> + +<H3>Buffer operation</H3> +<table> +<TR><TD WIDTH=100>B<TD>Back to the previous buffer +<TR><TD>v<TD>View HTML source +<TR><TD>s<TD>Select buffer +<TR><TD>E<TD>Edit buffer source +<TR><TD>R<TD>Reload buffer +<TR><TD>S<TD>Save buffer +<TR><TD>ESC s<TD>Save source +<TR><TD>ESC e<TD>Edit buffer image +</table> + +<H3>Buffer selection mode</H3> +<table> +<TR><TD WIDTH=100>k, C-p<TD>Select previous buffer +<TR><TD>j, C-n<TD>Select next buffer +<TR><TD>D<TD>Delect current buffer +<TR><TD>RET<TD>Go to the selected buffer +</table> + +<H3>Bookmark operation</H3> +<table> +<TR><TD WIDTH=100>ESC b<TD>Load bookmark +<TR><TD>ESC a<TD>Add current to bookmark +</table> + +<H3>Search</H3> +<table> +<TR><TD WIDTH=100>/,C-s<TD>Search forward +<TR><TD>?,C-r<TD>Search backward +<TR><TD>n<TD>Search next +<TR><TD>N<TD>Search previous +</table> + +<H3>Mark operation</H3> +<table> +<TR><TD WIDTH=100>C-SPC<TD>Set/unset mark +<TR><TD>ESC p<TD>Go to previous mark +<TR><TD>ESC n<TD>Go to next mark +<TR><TD>"<TD>Mark by regular expression +</table> + +<H3>Miscellany</H3> +<table> +<TR><TD WIDTH=100>!<TD>Execute shell command +<TR><TD>H<TD>Help (load this file) +<TR><TD>o<TD>Set option +<TR><TD>C-c<TD>Stop +<TR><TD>C-z<TD>Suspend +<TR><TD>q<TD>Quit (with confirmation, if you like) +<TR><TD>Q<TD>Quit without confirmation +</table> + +<H3>Line-edit mode</H3> +<table> +<TR><TD WIDTH=100>C-f<TD>Move cursor forward +<TR><TD>C-b<TD>Move cursor backward +<TR><TD>C-h<TD>Delete previous character +<TR><TD>C-d<TD>Delete current character +<TR><TD>C-k<TD>Kill everything after cursor +<TR><TD>C-u<TD>Kill everything before cursor +<TR><TD>C-a<TD>Move to the top of line +<TR><TD>C-e<TD>Move to the bottom of line +<TR><TD>C-p<TD>Fetch the previous string from the history list +<TR><TD>C-n<TD>Fetch the next string from the history list +<TR><TD>TAB,SPC<TD>Complete filename +<TR><TD>RETURN<TD>Accept +</table> + +<hr> +<a name="Key:lynx"></a> +<h2>Lynx-like key binding</h2> + +If you have chosen `Lynx-like key binding' at the compile time, +you can use the following key binding. + +<H3>Page/Cursor motion</H3> +<table> +<TR><TD WIDTH=100>SPC,C-v<TD>Forward page +<TR><TD>b,ESC v<TD>Previous page +<TR><TD>l<TD>Cursor right +<TR><TD>h<TD>Cursor left +<TR><TD>j<TD>Cursor down +<TR><TD>k<TD>Cursor up +<TR><TD>J<TD>Roll up one line +<TR><TD>K<TD>Roll down one line +<TR><TD>><TD>Shift screen right +<TR><TD><<TD>Shift screen left +<TR><TD>.<TD>Shift screen one column right +<TR><TD>,<TD>Shift screen one column left +<TR><TD>G<TD>Go to the specified line +<TR><TD>TAB,C-n,Down arrow<TD>Move to next hyperlink +<TR><TD>ESC TAB,C-p,Up arrow<TD>Move to previous link +</table> + + +<H2>Hyperlink operation</H2> +<table> +<TR><TD WIDTH=100>RET, C-f, Right arrow<TD>Follow hyperlink +<TR><TD>d, ESC RET<TD>Save link to file +<TR><TD>u<TD>Peek link URL +<TR><TD>I<TD>View inline image +<TR><TD>ESC I<TD>Save inline image to file +<TR><TD>:<TD>Mark URL-like strings as anchors +<TR><TD>ESC :<TD>Mark Message-ID-like strings as news anchors +<TR><TD>c<TD>Peek current URL +<TR><TD>=<TD>Display infomation about current document +<TR><TD>F<TD>Render frame +<TR><TD>M<TD>Browse current document using external browser +(use 2M and 3M to invoke second and third browser) +<TR><TD>ESC M<TD>Browse link using external browser +(use 2ESC M and 3ESC M to invoke second and third browser) +</table> + +<H2>File/Stream operation</H2> +<table> +<TR><TD WIDTH=100>g,U<TD>Open URL +<TR><TD>V<TD>View new file +<TR><TD>@<TD>Execute shell command and load +<TR><TD>#<TD>Execute shell command and browse +</table> + +<H2>Buffer operation</H2> +<table> +<TR><TD WIDTH=100>B, C-b, Left arrow<TD>Back to the previous buffer +<TR><TD>\<TD>View HTML source +<TR><TD>s, C-h<TD>Select buffer +<TR><TD>E<TD>Edit buffer source +<TR><TD>R, C-r<TD>Reload buffer +<TR><TD>S, p<TD>Save buffer +<TR><TD>ESC s<TD>Save source +<TR><TD>ESC e<TD>Edit buffer image +</table> + +<H2>Buffer selection mode</H2> +<table> +<TR><TD WIDTH=100>k, C-p<TD>Select previous buffer +<TR><TD>j, C-n<TD>Select next buffer +<TR><TD>D<TD>Delect current buffer +<TR><TD>RET<TD>Go to the selected buffer +</table> + +<H2>Bookmark operation</H2> +<table> +<TR><TD WIDTH=100>v, ESC b<TD>Load bookmark +<TR><TD>a, ESC a<TD>Add current to bookmark +</table> + +<H2>Search</H2> +<table> +<TR><TD WIDTH=100>/<TD>Search forward +<TR><TD>?<TD>Search backward +<TR><TD>n<TD>Search next +</table> + +<H2>Mark operation</H2> +<table> +<TR><TD WIDTH=100>C-SPC<TD>Set/unset mark +<TR><TD>ESC p<TD>Go to previous mark +<TR><TD>ESC n<TD>Go to next mark +<TR><TD>"<TD>Mark by regular expression +</table> + +<H2>Miscellany</H2> +<table> +<TR><TD WIDTH=100>!<TD>Execute shell command +<TR><TD>H<TD>Help (load this file) +<TR><TD>o<TD>Set option +<TR><TD>C-c<TD>Stop +<TR><TD>C-z<TD>Suspend +<TR><TD>q<TD>Quit (with confirmation, if you like) +<TR><TD>Q<TD>Quit without confirmation +</table> + +<H2>Line-edit mode</H2> +<table> +<TR><TD WIDTH=100>C-f<TD>Move cursor forward +<TR><TD>C-b<TD>Move cursor backward +<TR><TD>C-h<TD>Delete previous character +<TR><TD>C-d<TD>Delete current character +<TR><TD>C-k<TD>Kill everything after cursor +<TR><TD>C-u<TD>Kill everything before cursor +<TR><TD>C-a<TD>Move to the top of line +<TR><TD>C-e<TD>Move to the bottom of line +<TR><TD>C-p<TD>Fetch the previous string from the history list +<TR><TD>C-n<TD>Fetch the next string from the history list +<TR><TD>TAB,SPC<TD>Complete filename +<TR><TD>RETURN<TD>Accept +</table> + +<hr> +<a name="Mouse"></a> +<h2>Mouse operation</h2> +If w3m is compiled with mouse option and you are using +xterm/kterm/rxvt (in this case, you have to set the TERM +environment variable to `xterm' or `kterm'.) or GPM +environment, you can use mouse +for the navigation. +<p> +<table border=0> +<tr><td>left click +<td>Move the cursor to the place pointed by the mouse cursor. +If you click the cursor and it is on an anchor, follow the anchor. +<tr><td>middle click +<td>Back to the previous buffer. +<tr><td>right click +<td>Open pop-up menu. You can choose an item by clicking it. +<tr><td>left drag +<td>Scroll document. The default behavior is to grab the document +and drag it. You can reverse the behavior (grab the window and drag it) +with the option setting panel. +</table> +<p> + + +<hr> +<a name="Key:custom"></a> +<h2>Key customization</h2> +You can customize the key binding (except line-editing keymap) +by describing ~/.w3m/keymap. For example, +<pre> + + keymap C-o NEXT_PAGE + +</pre> +binds `NEXT_PAGE' function (normally bound to SPC and C-v) +to control-o. See <a href="README.func">README.func</a> for +list of available functions. Original and Lynx-like keymap +definitions are provided (<a href="keymap.default">keymap.default</a> +and <a href="keymap.lynx">keymap.lynx</a>) as examples. + +<hr> +<a name="LocalCGI"></a> +<h2>Local CGI</h2> +You can run CGI scripts using w3m, without any HTTP server. +It means that w3m behaves like an HTTP server and activates CGI script, +then w3m reads the output of the script and display it. The +<a href="file:///$LIB/w3mbookmark?mode=panel&bmark=~/.w3m/bookmark.html&url=MANUAL.html&title=w3m+manual">bookmark registration</a> +and <a href="file:///$LIB/w3mhelperpanel?mode=panel">helper-app editor</a> +are realized as local CGI scripts. +Using local CGI, w3m can be used as a general purpose form interface. +<P> +For security reason, CGI scripts invoked by w3m must be in one of +these directories. +<ul> +<li>The directory where w3m-related files are stored +(typically /usr/local/lib/w3m). This directory can be referred +as $LIB. +<li>/cgi-bin/ directory. You can map /cgi-bin/ to any directory you like +with option setting panel (``Directory corresponds to /cgi-bin'' field). +You can specify multiple paths separated by `:', like +/usr/local/cgi-bin:/home/aito/cgi-bin. It is not recommended to include +current directory to this path. +</ul> +<p> +The CGI script can use special header `w3m-control:' to control w3m. +This field can take any function (see <a href="README.func">README.func</a>), +and the specified function is invoked after the document is displayed. +For example, The CGI output +<pre> + +Content-Type: text/plain +W3m-control: BACK + +</pre> +will display blank page and delete that buffer immediately. +This is useful when you don't want to display any page after +the script is invoked. The next example +<pre> + +Content-Type: text/plain +W3m-control: DELETE_PREVBUF + +contents..... +</pre> +will override the current buffer. +<p> +One w3m-control: header have to contain only one function, but you can +include more than one w3m-control: lines in the HTTP header. +In addition, you can specify an argument to GOTO function: +<pre> + +Content-Type: text/plain +W3m-control: GOTO http://www.yahoo.com/ + +</pre> +This example works exactly the same way to the Location header: +<pre> + +Content-Type: text/plain +Location: http://www.yahoo.com/ + +</pre> +Note that this header has no effect when the CGI script is invoked +through HTTP server. + +</body> +</html> diff --git a/doc/README b/doc/README new file mode 100644 index 0000000..72246d1 --- /dev/null +++ b/doc/README @@ -0,0 +1,106 @@ + w3m: WWW wo Miru Tool version beta-990323 + (C) Copyright by Akinori ITO March 23, 1999 + +1. Introduction + + w3m is a pager with WWW capability. It IS a pager, but it can be +used as a text-mode WWW browser. + + The features of w3m are as follows: + +* When reading HTML document, you can follow links and view images + (using external image viewer). +* It has 'internet message mode', which determines the type of document + from header. If the Content-Type: field of the document is text/html, + that document is displayed as HTML document. +* You can change URL description like 'http://hogege.net' in plain text + into link to that URL. + +Current problems are: + +* Resize behaviour is imcomplete. +* It can't show images inline. (It seems to be impossible as far as using + xterm) +* It doesn't decode MIME-body of the document. +* Online manuals are poor. + +w3m is known to work on these platforms. + + SunOS4.1.x + HP-UX 9.x, 10.x + Solaris2.5.x + Linux 2.0.30 + FreeBSD 2.2.8, 3.1, 3.2 + NetBSD/macppc, m68k + EWS4800 Rel.12.2 Rev.A + Digital UNIX: v3.2D, v4.0D + IRIX 5.3, IRIX 6.5 + OS/2 with emx + Windows 9x/NT with Cygwin32 b20.1 + MS-DOS with DJGPP and WATT32 packet driver + MacOS X Server + +2. Installation + +Follow these instructions to install w3m. + +2.1 Run configure. The script will ask you a few questions. Answer them. +2.2 do make +2.3 do make install + +MACHINE/OS specific notices: + +HP-UX + If you want to use HP C compiler, answer + + Input your favorite C-compiler. + (Default: cc) cc -Aa -D_HPUX_SOURCE + + If you use just 'cc' without options, you can't compile w3m. + If you are using gcc, no option is needed. + +OS/2 + You can compile w3m using emx. First you have to do + is to compile GC library with + + cd gc + make -f EMX_MAKEFILE + + then compile w3m. I heard that OS/2 console can't + display color, you had better compile w3m without + color capability. + +Windows + See README.cygwin. + +MS-DOS + See README.dj. + + +3. Copyright + + (C) Copyright 1994-1999 by Akinori Ito. + + Hans J. Boehm, Alan J. Demers, Xerox Corp. and Silicon Graphics + have the copyright of the GC library comes with w3m package. + +4. License + + Use, modification and redistribution of this software is hereby granted, + provided that this entire copyright notice is included on any copies of + this software and applications and derivations thereof. + + This software is provided on an "as is" basis, without warranty of any + kind, either expressed or implied, as to any matter including, but not + limited to warranty of fitness of purpose, or merchantability, or + results obtained from use of this software. + + +5. Author + +Feel free to send your opinion to the author. + + Akinori Ito + Faculty of Engineering, Yamagata University + aito@ei5sun.yz.yamagata-u.ac.jp + http://ei5nazha.yz.yamagata-u.ac.jp/ diff --git a/doc/README.cygwin b/doc/README.cygwin new file mode 100644 index 0000000..b00545c --- /dev/null +++ b/doc/README.cygwin @@ -0,0 +1,18 @@ +***How to compile w3m on Windows*** + +To compile w3m on MS-Windows, you have to use Cygwin32 with development +tools. You can get it from http://sourceware.cygnus.com/cygwin/ . + +After installing Cygwin32, what you have to do first is + + TERM=ansi; export TERM + sh configure + +and + + make + + +Known Bugs: + +Local file with drive letter (//C/zonk.html) can't be handled correctly. diff --git a/doc/README.dict b/doc/README.dict new file mode 100644 index 0000000..63fd4a2 --- /dev/null +++ b/doc/README.dict @@ -0,0 +1,39 @@ +Dictionary look-up hack for w3m + +1. INTRODUCTION + +If you have dictionary look-up command (like 'webster'), you can +look a word in a document using w3m. This dictionary-lookup code +was contributed by `Rubikitch' (rubikitch@ruby-lang.org). + +2. INSTALL + +To make use of dictionary look-up, you have to change compile +option by hand. After running configure, edit config.h and +change + +#undef DICT + +into + +#define DICT + +and recompile w3m. (You have to recompile dict.c and keybind.c.) + +Then prepare a command named 'w3mdict.' For example, if you want +to use 'webster' command, do the following: + +% cd /usr/local/bin +% ln -s `which webster` w3mdict + +In general, w3mdict can be any command that takes a word as an +argument and outputs something onto stdout. + +3. USAGE + +You can use the following two commands. + +ESC w Input a word and look it up using w3mdict command. + +ESC W look up the current word in the buffer. + diff --git a/doc/README.func b/doc/README.func new file mode 100644 index 0000000..7b2bc6b --- /dev/null +++ b/doc/README.func @@ -0,0 +1,85 @@ +ABORT Quit w3m without confirmation +ADD_BOOKMARK Add current page to bookmark +BACK Back to previous buffer +BEGIN Go to the first line +BOOKMARK Read bookmark +CENTER_H Move to the center line +CENTER_V Move to the center column +COOKIE View cookie list +DELETE_PREVBUF Delete previous buffer +DICT_WORD Execute dictionary command (see README.dict) +DOWN Scroll down one line +DOWN_LOAD Save HTML source +EDIT Edit current document +EDIT_SCREEN Edit currently rendered document +END Go to the last line +EXEC_SHELL Execute shell command +EXIT Quit w3m without confirmation +EXTERN Execute external browser +EXTERN_LINK View current link using external browser +FRAME Render frame +GOTO Go to URL +GOTO_LINE Go to specified line +GOTO_LINK Go to current link +HELP View help +HISTORY View history of URL +INFO View info of current document +INTERRUPT Stop loading document +INIT_MAILCAP Reread mailcap +LEFT Shift screen one column +LINE_BEGIN Go to the beginning of line +LINE_END Go to the end of line +LINE_INFO Show current line number +LOAD Load local file +MAIN_MENU Popup menu +MARK Set/unset mark +MARK_MID Mark Message-ID-like strings as anchors +MARK_URL Mark URL-like strings as anchors +MENU Popup menu +MOVE_DOWN Move cursor down +MOVE_LEFT Move cursor left +MOVE_RIGHT Move cursor right +MOVE_UP Move cursor up +NEXT_LINK Move to next link +NEXT_MARK Move to next word +NEXT_PAGE Move to next page +NEXT_WORD Move to next word +NOTHING Do nothing +NULL Do nothing +OPTIONS Option setting panel +PEEK Peek current URL +PEEK_LINK Peek link URL +PEEK_IMG Peek image URL +PIPE_SHELL Execute shell command and browse +PREV_LINK Move to previous link +PREV_MARK Move to previous mark +PREV_PAGE Move to previous page +PREV_WORD Move to previous word +PRINT Save buffer to file +QUIT Quit w3m +READ_SHELL Execute shell command and load +REDRAW Redraw screen +REG_MARK Set mark using regexp +RELOAD Reload buffer +RIGHT Shift screen one column right +SAVE Save HTML source to file +SAVE_IMAGE Save image to file +SAVE_LINK Save link to file +SAVE_SCREEN Save rendered document to file +SEARCH Search forward +SEARCH_BACK Search backward +SEARCH_FORE Search forward +SEARCH_NEXT Search next regexp +SEARCH_PREV Search previous regexp +SELECT Go to buffer selection panel +SHELL Execute shell command +SHIFT_LEFT Shift screen left +SHIFT_RIGHT Shift screen right +SOURCE View HTML source +SUSPEND Stop loading document +UP Scroll up one line +VIEW View HTML source +VIEW_BOOKMARK View bookmark +VIEW_IMAGE View image +WHEREIS Search forward +WRAP_TOGGLE Toggle wrap search mode diff --git a/doc/STORY.html b/doc/STORY.html new file mode 100644 index 0000000..14a3c08 --- /dev/null +++ b/doc/STORY.html @@ -0,0 +1,209 @@ +<html> +<head> +<title>History of w3m</title> +</head> +<body> +<h1>History of w3m</h1> +<div align=right> +1999/2/18<br> +1999/3/8 revised<br> +1999/6/11 translated into English<br> +Akinori Ito<br> +aito@ei5sun.yz.yamagata-u.ac.jp +</div> +<h2>Introduction</h2> +W3m is a text-based pager and WWW browser. +It is similar application to the famous text-based +browser <a href="http://www.lynx.browser.org/">Lynx</a>. +However, w3m has several advantages against Lynx. For example, +<UL> +<LI>W3m can render tables. +<LI>W3m can render frame (by converting frame into table). +<LI>As w3m is a pager, it can read document from standard input. +(I heard Lynx also can display standard-input-given document, like this: +<pre> + lynx /dev/fd/0 > file +</pre> +Hmm, it works on Linux. ) +<LI>W3m is small. Its stripped binary for Sparc (compiled with +gcc -O2, version beta-990217) is only 260kbyte, while binary size +of Lynx is beyond 1.8Mbyte. (Actually, lynx it 800K on my i386 system, w3m is 200K + libgc.) +</UL> +It is true that Lynx is an excellent browser, who have many +features w3m doesn't have. For example, +<UL> +<LI>Lynx can handle cookies. +<LI>Lynx has many options. +<LI>Lynx is multilingual. (W3m is Japanese-English bilingual) +</UL> +etc. It is also a great advantage that Lynx has a lot of +documentation. +<P> +<b>I don't intend w3m to be a substitute of any other browsers, +including Netscape and Lynx.</b> Why did I wrote w3m? +Because I felt inconvenient with conventional browsers +to `take a look' at web pages. +I am browsing web pages in LAN environment. When I want to take +a glance at a web page, I don't want to wait to start up Netscape. +Lynx also takes a few seconds to start up (you can get lynx startup time to almost zero when you rm /etc/mailcap). On the other hand, +w3m starts immediately with little load to the host machine. +After looking at the information using w3m, I use other browser +if I want to read the the page in detail. As for me, however, +w3m is enough to read most of web pages. + +<h2>The birth of w3m</h2> +<P> +w3m was derived from a pager named `fm'. Fm was written before +1991 (I don't remember the exact date) when WWW was not popular. +At that time, the word `browser' meant a file browser like +`more' or `less'. +<P> +I wrote fm to debug a program for my research. To trace the status +of the program, it dumped megabytes of values of variables into a file, +and I debugged it by checking the dumped file. The program dumped +information at a certain time in one line, which made the dumped line +several hundred characters long. When I looked the file using `more' or +`less', one line was folded into several lines and it was very hard +to read it. Therefore, I wrote fm, which didn't fold a line. Fm displayed +one logical line as one physical line. When seeing the hidden +part of a line, fm shifted entire screen. As I used 80x24 terminal at that +time, fm was very useful for the debugging. +<P> +Several years later, I got to know WWW and began to use it. +I used XMosaic and Chimera. I liked Chimera because it was light. +As I was interested in the mechanism of WWW, I learned HTML and +HTTP, and I felt it simpler than I expected. The earlier version +of HTTP was very similar to Gopher protocol. HTML 2.0 was +simple enough to render. All I have to do seemed to be line folding +and itemized display. Then I made a little modification to fm +and made a web browser. It was the first version of w3m. +The name `w3m' was an abbreviation of Japanese phrase `WWW wo miru', +which means `see WWW'. It was an inheritance from `fm', which +was an abbreviation of `File wo miru'. The first version of w3m +was released at the beginning of 1995. + +<h2>Death and rebirth of w3m</h2> +<p> +I had used w3m as a pager to read files, E-mails and online manuals. +It was a substitute of less. Sometimes I used w3m as a web browser, +but there were many pages w3m couldn't display correctly, most of +which used table for page layout. Once I tried to implement table +renderer, but I gave up because it seemed to be too difficult for me. +<P> +It was 1998 when I tried to modify w3m again. There were two reasons. +The first is that I had some time to do it. I stayed Boston University +as a visiting researcher at that time. The second reason is that +I wanted to use table in my personal web page. I had written research +log using HTML, and I wanted to write a table in it. At first I used +<pre>..</pre> to describe table, but it was not cool at all. +One day I used <table> tag, which made me to use Netscape to +read the research log. Then I decided to implement a table renderer +into w3m. +<P> +I didn't intend to write a perfect table renderer because tables +I used was not very complicated. However, imcomplete table rendering +made the display of table-layout pages horrible. I realized that +it required almost-perfect table renderer +to do well both in `rendering (real) table' and `fine display of +table-layout page.' It was a thorn path. +<P> +After taking several months, I finished `fair' table renderer. +Then I implemented form into w3m. Finally, w3m was reborn as a +practical web browser. + +<h2>Table rendering algorithm in w3m</h2> + +HTML table rendering is difficult. Tabular environment +of LaTeX is not very difficult, which makes the width of a column +either a specified value or the maximum width to put items into it. +On the other hand, HTML table renderer has to decide +the width of a column so that the entire table can fit into the +display appropriately, and fold the contents of the table according +to the column width. Inappropriate column width decision makes +the table ugly. Moreover, table can be nested, which makes the algorithm +more complicated. + +<OL> +<LI>First, calculate the maximum and minimum width of each column. +The maximum width is the width required to display the column +without folding the contents. Generally, it is the length of +paragraph delimited by <BR> or <P>. +The minimum width is the lower limit to display the contents. +If the column contains the word `internationalization', the minimum +width will be 20. If the column contains +<pre>..</pre>, the maximum width of the preformatted +text will be the minimum width of the column. + +<LI>If the width of the column is specified by WIDTH attribute, +fix the column width using that value. If the specified width is +smaller than the minimum width of the column, fix the column width +to the minimum width. + +<LI>Calculate the sum of the maximum width (or fixed width) of +each column and check if the sum exceeds the screen width. +If it is smaller than screen width, these values are used for +width of each column. + +<LI>If the sum is larger than the screen width, determine the widths +of each column according to the following steps. +<OL> +<LI>Let W be the screen width subtracted by the sum of widths of +fixed-width columns. +<LI>Distribute W into the columns whose width are not decided, +in proportion to the logarithm of the maximum width of each column. +<li>If the distributed width of a column is smaller than the minimum width, +then fix the width of the column to the minimum width, and +do the distribution again. +</OL> +</OL> + +In this process, distributed width is proportion to logarithm of +maximum width, but I am not sure that this heuristic is the best. +It can be, for example, square root of the maximum width. +<P> +The algorithm above assumes that the screen width is known. +But it is not true for nested table. According the algorithm above, +the column width of the outer table have to be known to render +the inner table, while the total width of the inner table have to +be known to determine the column width of the outer table. +If WIDTH attribute exists there are no problems. Otherwise, w3m +assumes that the inner table is 0.8 times as wide as the outer +table. It works fine, but if there are two tables side by side in an outer +table, the width of the outer table always exceeds the screen width. +To render this kind of table correctly, one have to render the table once, +check the width of outmost table, and then render the entire table again. +Netscape might employ this kind of algorithm. + +<h2>Libraries</h2> + +w3m uses +<a href="http://reality.sgi.com/boehm/gc.html">Boehm GC</a> +library. This library was written by H. Boehm and A. Demers. +I could distribute w3m without this library because one can +get the library separately, but I decided to contain it in the +w3m distribution for the convenience of an installer. +W3m doesn't use libwww. +<P> +Boehm GC is a garbage collector for C and C++. I began to use this +library when I implemented table, and it was great. I couldn't +implement table and form without this library. +<P> +Older version than beta-990304 used +<a href="http://home.cern.ch/~orel/libftp/libftp/libftp.html">LIBFTP</a> +because I felt tired of writing codes to handle FTP protocol. +But I rewrote the FTP code by myself to make w3m completely free. +It made w3m slightly smaller. +<P> +By the way, w3m doesn't use UNIX standard regexp library and curses library. +It is because I want to use Japanese. When I wrote fm, there were +no free regexp/curses libraries that can treat Japanese. Now both libraries +are available and they looks faster than w3m code. + +<h2>Future work</h2> + +...Nothing. As w3m's virtues are its small size and rendering speed, +adding more features might lose these advantages. On the other hand, +w3m is still known to have many bugs, and I will continue fixing them. + +</body> +</html> diff --git a/doc/history b/doc/history new file mode 100644 index 0000000..5f0c62d --- /dev/null +++ b/doc/history @@ -0,0 +1,1750 @@ +2001/1/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01667] Re: mailer %s + +2001/1/24 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01661] Re: <head> + security fix. + + +2001/1/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * check if ", <, > and &s are quoted within attribute values. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01663] replace addUniqHist with addHist in loadHistory() + + +2001/1/22 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01617] Re: first body with -m (Re: w3m-m17n-0.7) + * terminal resize related fix. + * info page ('=' command) fix for multi-layered frame page. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01621] NEXT_LINK and GOTO_LINE problem + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01623] Re: (frame) http://www.securityfocus.com/ +Subject: [w3m-dev 01632] Re: (frame) http://www.securityfocus.com/ + frame fix. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01624] Re: first body with -m +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01625] Re: first body with -m + pgFore, pgBack behaviour fix. + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01635] Directory list + local.c directory list fix. + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01643] buffername +Subject: [w3m-dev 01650] Re: buffername + buffername (title) related improvements. + * when displayLink is ON, truncate buffername on showing long URL. + * displayBuffer() cleanup. + * remove trailing spaces from content of <title>..</title>. + * [w3m-dev 01503], [w3m-dev 01504] + +From: Hironori Sakamoto <h-saka@udlew10.uldev.lsi.nec.co.jp> +Subject: [w3m-dev 01646] putAnchor + * putAnchor related improvement. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01647] Re: first body with -m + * cursor position moves unexpectedly when reloading a URL + with #label. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01651] display column position with LINE_INFO + +2001/1/5 + +From: Ryoji Kato <ryoji.kato@nrj.ericsson.se> +Subject: [w3m-dev 01582] rfc2732 patch + literal IPv6 address treatment (bracketed by '[' and ']') + according to RFC2732. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01594] first body with -m (Re: w3m-m17n-0.7) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01602] Re: first body with -m (Re: w3m-m17n-0.7) + +2001/1/1 + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01584] Re: attribute replacing in frames. (Re: some fixes) + + +2000/12/27 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * remove trailing blank lines in a buffer. + +2000/12/26 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01560] Re: long URL + Multiple 'u' and 'c' scrolls long URL. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01570] Re: long URL + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01506] compile option of gc.a + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01509] Forward: Bug#79689: No way to view information on SSL certificates + Now '=' shows info about SSL certificate. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01556] Re: ANSI color support (was Re: w3m-m17n-0.4) + ANSI color support. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01535] how to check wait3 in configure. +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01537] Re: how to check wait3 in configure. + On BSD/OS 3.1, SunOS 4.1.3, configure can't detect wait3(). + +2000/12/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + <plaintext> doesn't work. + + +2000/12/22 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01555] Re: some fixes for <select> + w3m crashes by <select> without <option>. + +2000/12/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table related improvements (<xmp> inside table, etc) + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01536] Re: <P> in <DL> +Subject: [w3m-dev 01544] Re: <P> in <DL> + * w3m crashes by an illegal HTML. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * treat unclosed <a>, <img_alt>, <b>, <u> + +2000/12/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * bugfix on <dt> tag processing in feed_table_tag(). + * w3m eventually crashed when a tag is not closed in a table. + * ignore <p> just after <dt>. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +Subject: [w3m-dev 01530] returned at a morment. + * skip newline within "-enclosed attribute value. + +Subject: [w3m-dev 01531] coocie check in header from stdin. + * w3m crashes by 'cat mail | w3m -m' + + +2000/12/17 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01513] Re: w3m-0.1.11-pre-kokb23 + frame.c bugfix +Subject: [w3m-dev 01515] some fixes for <select> +Subject: [w3m-dev 01516] Re: some fixes for <select> + Several improvements on <select>..<option> + +2000/12/14 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01501] Re: w3m-0.1.11-pre-kokb23 + Compile error for 'no menu' model + + +2000/12/13 + +From: sekita-n@hera.im.uec.ac.jp (Nobutaka SEKITANI) +Subject: [w3m-dev 01483] Patch to show image URL includes anchor + Peek image URL by 'i' + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01500] fix risky code in url.c + Vulnerble code in url.c fixed + +2000/12/12 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01491] bug ? + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + Search for a string that contains null character. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01498] Re: null character + Infinite loop + + +2000/12/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * StrmyISgets doesn't recognize a '\r' as a newline character. + * Null character support on pager mode. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01487] A string in <textarea> is broken after editing + <textarea> related fix. + +Subject: [w3m-dev 01488] buffer overflow bugs + * Buffer overflow fixes. + +2000/12/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * maximum_table_width now considers width attribute in td and th tag. + +2000/12/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01473] Re: internal tag and attribute check + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * hborder and border attribute handling. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01478] Option Setting Panel + * Improvement of the option setting panel view. + +2000/12/7 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * parse_tag improvements. + * don't parse tag within visible_length(). + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01456] linein.c + * linein.c is rewritten based on calcPosition(). + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01457] cursor position on sumbit form + * TAB key behaviour fix. + +2000/12/3 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01449] Re: Directory of private header of gc library. + * w3m crashes when accessing a list after popping the last element + by popText (rpopText). + +2000/12/2 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * image map related fix. + +2000/12/1 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: Security hole in w3m (<input_alt type=file>) + * Prohibit using internal tags in HTML. + +Subject: [w3m-dev 01432] Re: w3m-0.1.11-pre-kokb22 patch +Subject: [w3m-dev 01437] Re: w3m-0.1.11-pre-kokb22 patch + * Image map related fix. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * a compile option which enables the removal of trailing + blank lines in a burrer. (ENABLE_REMOVE_TRAILINGSPACES) + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev-en 00301] Re: "w3m -h" outputs to stderr + * Destination of w3m -h output is changed from stderr + to stdout. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01430] Re: w3m-0.1.11-pre-kokb22 patch + * EWS4800(/usr/abiccs/bin/cc) support. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * id attribute check in dummy_table tag. + * fid attribute check in form_int tag. + * table stack overflow check. + +2000/11/29 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01422] bpcmp in anchor.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01423] Re: bpcmp in anchor.c + * some improvements for speedup. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * CheckType() bugfix and speedup. + +2000/11/28 + +From: Takenobu Sugiyama <sugiyama@ae.advantest.co.jp> +Subject: patch for cygwin + * enables ftp download for cygwin w3m + +2000/11/27 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01401] Re: bugfix of display of control chars, merge of easy UTF-8 patch, etc. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table rendering speed-up. + + +2000/11/25 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table column width sometimes get narrower than specified width value. + +2000/11/24 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Progress bar display enhancement. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * command line option about proxy and cookie doesn't work. + * 'Save to local file' overwrites the existing file. + +2000/11/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * get_ctype is now macro. + * menu.c type mismatch fix. + + +2000/11/22 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * fixes for speedup. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01372] w3m sometimes uses the wrong mailcap entry + http://bugs.debian.org/77679 + +2000/11/20 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * an empty table in another table makes the outer table funny. + +2000/11/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + gc6 support. + +2000/11/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * space characters in a buffer are mapped into 0x80-0x9f. + * unprintable characters (0x80-0xa0) are displayed as \xxx. + +From: Tsutomu Okada ($B2,ED(B $BJY(B) <okada@furuno.co.jp> +Subject: [w3m-dev 01354] minimize when #undef USE_GOPHER or USE_NNTP + +2000/11/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + getescapechar() returns abnormal value for illegal entity. + +2000/11/15 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * table-related fix. + * DEL character is treated as `breakable space.' + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01338] Re: Lynx patch for character encoding in form +Subject: [w3m-dev 01342] Re: Lynx patch for character encoding in form + * support for accept-charset attribute in form tag. + +2000/11/14 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * cleanup_str and htmlquote_str now returns the oritinal string + itself when there's no need to (un)quote it. + +2000/11/10 + +From: Katsuyuki Watanabe <katsuyuki_1.watanabe@toppan.co.jp> +Subject: [w3m-dev 01336] patch for Cygwin 1.1.x + Patch for Cygwin 1.1.x (1.1.3 and later) + +2000/11/8 + +From: Jan Nieuwenhuizen <janneke@gnu.org> +Subject: [w3m-dev-en 00189] [PATCH] w3m menu <select> search + Enable to search within popup menu. + + +2000/11/7 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01331] Re: form TEXT: + * Search string history and form input string history are merged. + +2000/11/4 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * When a cell width exceeds the screen width, format contents in + the cell to fit into the screen width. + +2000/11/2 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01313] Re: SCM_NNTP + nntp: for MARL_URL + +2000/10/31 + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01310] Re: option select (Re: w3mmee-0.1.11p10) + Output error messages from gc library using disp_message_nsec. + +2000/10/30 + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Subject: [w3m-dev 01294] mouse no effect on blank page. +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01295] Re: mouse no effect on blank page. + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 01297] Re: backword search bug report + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01298] Re: backword search bug report + bug fix of backword search +Subject: [w3m-dev 01299] Re: backword search bug report + bug fix of the handling of multi-byte regexp. + +2000/10/29 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * LESSOPEN can be set via the option setting panel. (default: off) + * speed-up of gunzip_stream(), save2tmp(), visible_length(). + + +2000/10/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Emacs-like completion key support. + (by #define EMACS_LIKE_LINEEDIT in config.h) + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01284] Re: improvement of filename input + * in 'Goto URL' command, local file name will be completed + after 'file:/'. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01280] Stop to prepend rc_dir to full path. + +2000/10/27 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01269] Re: SCM_NNTP +Subject: [w3m-dev 01273] Re: SCM_NNTP + Prohibit gopher:, news: and nntp: scheme when USE_GOPHER and USE_NNTP + macros are undefined. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01258] improvement of filename input + * Completion lists are displayed by C-d. + * in 'Goto URL' command, local file name will be completed + after 'file:/', 'file:///' and 'file://localhost/'. + * password part of URLs in the history list are removed. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01277] Accept-Encoding: gzip (Re: some wishlists) + Accept-Encoding: gzip, compress + is appended in the request header. +Subject: [w3m-dev 01275] Re: squeeze multiple blank lines option ( http://bugs.debian.org/75527 ) + when #ifdef DEBIAN, + 'squeeze multiple blank line' switch (default -S) is set to -s + character-code specifier (-s/-e/-j) are removed. use '-o kanjicode={S,E,J}' + instead. +Subject: [w3m-dev 01274] Re: SCM_NNTP + nntp: support. +Subject: [w3m-dev 01276] URL in w3m -v + when LANG=EN (or #undef JP_CHARSET), the URL displayed at w3m -v + (visual startup mode) is incorrect. + +2000/10/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + location of mailcap and mime.type can be specified in the Option Setting + Panel. + +2000/10/25 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01247] Re: buffer selection menu + Menu related patches. + ([w3m-dev 01227], [w3m-dev 01228],[w3m-dev 01229], [w3m-dev 01237], + [w3m-dev 01238]) + +2000/10/24 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * in the cookie-related setting, '.' is regarded as any domain. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01240] Re: w3m-0.1.11-pre-kokb17 patch + 'incompatible pointer type' fix. + +2000/10/23 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * "Domains from which should accept/reject cookies" setting in + the option setting panel. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01211] Re: a small change to linein.c +Subject: [w3m-dev 01214] Re: a small change to linein.c + * When editing long string, a part of the string disappear. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01216] error message for invalid keymap +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01220] Re: error message for invalid keymap + * w3m will display an error-message against the illegal + keymap file specification. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01217] keymap.lynx example could be better. + keymap.lynx update. + +2000/10/20 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + cookie-related improvements. + * version 1 cookie handling is now compliant to + http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-12.txt + Cookie2 is added in the Netscape-style cookie request header. + +2000/10/19 + +From: "Ambrose Li [EDP]" <acli@mingpaoxpress.com> +Subject: [w3m-dev-en 00136] version 0 cookies and some odds and ends +Subject: [w3m-dev-en 00191] sorry, the last patch was not made properly +Subject: [w3m-dev-en 00190] w3m-0.1.10 patch (mostly version 0 cookie handling) + I've hacked up a big mess (patch) against w3m-0.1.9 primarily + involving version 0 cookies. To my dismay, it seems that most + servers out there still want version 0 cookies and version 0 + cookie handling behaviour, and w3m's cookie handling is too + strict for version 0, causing some sites (notably my.yahoo.co.jp) + not to work. + +2000/10/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * pixel-per-character is now changable. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01208] '#', '?' in ftp:/.... + * w3m fails to parse URL when ftp:/ URL contains '#'. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +Subject: [w3m-dev 01209] http_response_code and ``Location:'' header + w3m now follows Location: header only when + http_response_code is 301 - 303. + + +2000/10/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + local CGI makes zombie processes. + + +2000/10/16 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + w3m hangs when <textarea> is not closed in a table. + +From: maeda@tokyo.pm.org +Subject: [w3m-dev 00990] auth password input + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01193] Re: frame bug? + w3m eventually crashes when browsing frame pages. + +2000/10/13 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00928] misdetection of IPv6 support on CYGWIN 1.1.2 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01170] Re: cursor position after RELOAD, EDIT + * Bugfix: remove cache files + * The following functions can take arguments in keymap. + LOAD ... a file name + EXTERN, EXTERN_LINK ... a name of the external browser + (Can't be used from w3m-control: ) + EXEC_SHELL, READ_SHELL, PIPE_SHELL ... shell command + (Can't be used from w3m-control: ) + SAVE, SAVE_IMAGE, SAVE_LINK, SAVE_SCREEN ... a filename (or command name) + (Can't be used from w3m-control: ) + + +2000/10/11 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * the buffer name of an input taken from the stdin is now determined + from MAN_PN. + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01156] Re: w3m-0.1.11-pre-kokb15 + * mydirname bugfix. + * SERVER_NAME can be configured. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01158] some bugs fix when RELOAD, EDIT +Subject: [w3m-dev 01164] cursor position after RELOAD, EDIT + * a file: called as local CGI can be edited. + +2000/10/10 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01166] Re: cell width in table + table-related bugfix. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01155] history of data for <input type=text> + <input type=text> input will be put into history buffer. + +2000/10/9 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01150] Some bug fixes + Bugfixes of the problems reported in + [w3m-dev 00956] unknown scheme in frame + [w3m-dev 00975] goto link from frame page + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01145] buffer overflow in linein.c + Fix of the buffer overrun problem in inputLineHist(linein.c) + +2000/10/8 + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01136] function argument in keymap +Subject: [w3m-dev 01139] Re: function argument in keymap + Some functions specified in ~/.w3m/keymap can take an argument. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 01143] image map with popup menu + image map can be treated as popup menu + (#define MENU_MAP in config.h) + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 00971] Re: segmentation fault with http: + Specifying 'http:' or 'http:/' as URLs will crash w3m. + +2000/10/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01134] w3m in xterm horribly confused by Japanese in title (fr + w3m-en will crash when browsing a page with Japanese title. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01127] SIGINT signal in ftp session (Re: my w3m support page) + SIGINT will crash w3m when downloading via ftp. + +2000/10/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * the maximum value of wmin in recalc_width() is changed to 0.05. + * when deflating compressed data other than http file and local file, + the file will be stored as a temporary file. + * mailcap edit= attribute support. + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Improvements of -dump, -source_dump options. + * Ignore <meta> tags in a frame. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00930] HTML-quote in w3mbookmark.c + * In 'Bookmark registration', URL and Title are not HTML-quoted. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Subject: [w3m-dev 00972] better display of progress bar ? + * An improvement of progress bar. + +2000/10/05 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> + * Null pointer chech for textlist. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Subject: [w3m-dev 01100] space in URL + + * http://bugs.debian.org/60825 and http://bugs.debian.org/67466 + when submitting a form, name is not quoted (which should be) + + * http://bugs.debian.org/66887 + Remove preceding and trailing spaces from URL input through + 'U' command. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01113] bug fix (content charset) + content charset bugfix + + +2000/10/02 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01112] Re: mailcap test= directive + improvements of mailcap handling + * In addition to %s, %t (content-type name) will be available. + * nametemplate option is now valid. + * If there is no %s in the entry, 'command < %s' is assumed. + * If needsterminal is specified, spawn the command foreground. + * If copiousoutput is specified, load the command output into + buffer. + * `htmloutput' option is added, which indicates that the output + of the command is to be read by w3m as text/html. For example, + + application/excel; xlHtml %s | lv -Iu8 -Oej; htmloutput + + enables an Excel book to be displayed as an HTML document. + * compressed file browsing support for ftp scheme. + * Bug: compressed file isn't displayed properly for http: scheme. + +2000/09/28 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01097] gunzip_stream problem + * Fix of the behaviour against INT signal while reading compressed + file. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01092] CONFIG_FILE + * CONFIG_FILE in config.h was hard-coded. + +2000/09/17 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01078] treatment of content type + Improvements around document type handling. + * precedence between lessopen_stream() and gunzip_stream() was changed + in examineFile(). + * lessopen_stream()ed file is treated as a plein text. + * lessopen_stream() is applied only if document type is text/* or + no external viewer is set. + * all text/* data other than text/html are handled inside w3m. + * The document type displayed by page_info_panel() is now the one + before examineFile() processing. + * When invoking an external viewer, ">/dev/null 2>&1 &" is appended + to the command line. + +2000/09/13 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01053] Re: Location: in local cgi. + * Do not interpret Location: header of the local file when invoking + with -m flag, + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01065] map key '0' + Improvement around keymap. + * Now a single '0' can be mapped. Numbers other than 0, for example + `10 j' are regarded as prefix arguments. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 01066] 104japan + * Code conversion fix for forms in frame. + +2000/09/07 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01058] <dt>, <dd>, <blockquote> (Re: <ol> etc.) + * insert blank lines before and after <blockquote>. + * Don't ignore <p> tag just after <dt> and <dd>. + +2000/09/04 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01052] cellpadding, cellspacing, vspace, etc. + Some changes about space character and blank lines: + * <a name="..."></a> or <font> outside <tr> or <td> are pushed + into the next cell. + * cellspacing attribute in <table> tag is now handled correctly. + * vspace attribute interpretation. + * blank line detection criterion is changed. + * </p> tag inserts a blank line. + +2000/08/17 + +From: Tsutomu Okada <okada@furuno.co.jp> +Subject: [w3m-dev 01018] sqrt DOMAIN error in table.c +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01019] Re: sqrt DOMAIN error in table.c + * fix of DOMAIN error of sqrt(). + +2000/08/15 + +From: satodai@dog.intcul.tohoku.ac.jp (Dai Sato) +Subject: [w3m-dev 01017] value of input tag in option panel + * Fix of the problem of the option setting panel: when specifying + a value including a double quote, the value after " is not displayed + on the next invocation of the option setting panel. + +2000/08/06 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01016] Table geometry calculation + * rounding algorithm of table geometry calculation is changed to + minimize the difference of the column width and the `true' width. + +2000/07/26 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01006] initialize PRNG of openssl 0.9.5 or later + * when using openssl library after 0.9.5, enables SSL on the environment + without the random device (/dev/urandom). + +2000/07/21 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01004] unused socket is not closed. + When interrupting file transfer using C-c, the socket sometimes + stay unclosed. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01005] table caption problem + Fix of the problem that w3m doesn't stop when there's no closing + </caption>. + +2000/07/19 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00966] ssl and proxy authorization + Fix of the authorization procedure of SSL tunneling via HTP proxy. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 01003] Some bug fixes for table + +2000/07/16 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00999] Re: bookmark + * Sometimes a link can't be appended into the bookmark. + +2000/06/18 + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +Subject: [w3m-dev 00934] clear_buffer bug + Bugfix: when clear_buffer is TRUE, selBuf() clears the screen. + +2000/06/17 + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +Subject: [w3m-dev 00929] ftp.c patch + Return code 230 against USER command is regarded as a successful + login. + +2000/06/16 + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00923] some bug fixes + * when #undef JP_CHARSET, file.c doesn't compile. + * buffer.c bugfix ("=" should be "==") + +From: Kazuhiko Izawa <izawa@nucef.tokai.jaeri.go.jp> +Subject: [w3m-dev 00924] Re: w3m-0.1.11pre +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Subject: [w3m-dev 00925] Re: w3m-0.1.11pre + Accessing URL like file://localhost/foo causes abnormal termination. + +2000.6.6 +From: aito +* [w3m-dev 00826] + * Bugfix: When a header by CGI POST method gives Location: header, + the redirect can't be reloaded. + * white spaces in URL are removed. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00827] Bugfix: onA() doesn't work. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00835] Improvement of 'Jump to label' behavior within a frame. + +2000.6.5 +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00789] Bugfix: width of <li> +* [w3m-dev 00801] Bugfix: Line break occurs on . +* [w3m-dev 00813] Bugfix: single > in a document isn't analyzed properly. +* [w3m-dev 00818][w3m-dev 00819] Bugfix: <xmp>,<listing> in <textarea> +* [w3m-dev 00820] Screen shift bugfix. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00807] Bugfix: <option> without <select> in table causes core dump. +* [w3m-dev 00816] Bugfix: CRs in <textarea>..</textarea> are replaced with + white spaces. + +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00814] Bugfix: After specifying non-text file in 'V' command, + w3m dumps core. + +2000.6.1 +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00581] BUFINFO related bugfix. +* [w3m-dev 00641] Bugfix: extbrowser setting in config desn't work. +* [w3m-dev 00660] Bugfix: pathname to invoke external viewer becomes like + ``/home/okada/.w3m//w3mv6244-0..pdf''. +* [w3m-dev 00701] enhancement of [w3m-dev 00684]. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00582] Bugfix: kterm mouse, etc. +* [w3m-dev 00586] Bugfix; when CLEAR_BUF is defined, buffer size is displayed as [0 line]. +* [w3m-dev 00605] + * show_params() improvement. + * when CLEAR_BUF is defined and reloading local file, that is overwritten. +* [w3m-dev 00606] When submitting data in textarea without editing them, CR charcters are + sent instead of CRLF. +* [w3m-dev 00630] Bugfix of mouse-dragging behaviour. +* [w3m-dev 00654] [w3m-dev 00666] When CLEAR_BUF is defined, content of form disappears. +* [w3m-dev 00677] [w3m-dev 00704] Improvement of Japanese coding-system decition algorithm. +* [w3m-dev 00684] Command line analysis enhancement. +* [w3m-dev 00696] Bugfix of PIPE_SHELL('#'), READ_SHELL('@') and EXEC_SHELL('!'). +* [w3m-dev 00706] Bugfix: When CLEAR_BUF is defined, anchors created by : disappears. +* [w3m-dev 00720] Enhancement of dirlist.cgi. +* [w3m-dev 00724] when -m option is used, continuation lines in header are not + processed properly. +* [w3m-dev 00728] handling of Japanese character in HTTP header. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00589] Bugfix: w3m dumps core after like w3m -T a -B and save command. +* [w3m-dev 00595][w3m-dev 00610] frameset related bugfix. +* [w3m-dev 00631][w3m-dev 00633] ID_EXT related bugfix. +* [w3m-dev 00632] Bugfix? handling of character-entity (") in attribute. +* [w3m-dev 00646] Enhancement: frame names are embedded as id attribute in + the frame-table. +* [w3m-dev 00680] +* [w3m-dev 00683] Bugfix: <STRONG> tags become comments in frame. +* [w3m-dev 00707] frame related bugfix. +* [w3m-dev 00774] Bugfix: as some file descriptors are not closed, file descriptors + are exhausted on a certain condition. + +From: SASAKI Takeshi <sasaki@sysrap.cs.fujitsu.co.jp> +* [w3m-dev 00598] ID_EXT related bugfix. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00602] Bugfix: a blank line is inserted when processing <title>...</title>. +* [w3m-dev 00617] <table> within <blockquote> in <table> corrupts. +* [w3m-dev 00675] Support of terminals which can't display (0xa0). +* [w3m-dev 00732] <!--comment --\n> like comment handling fix. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +* [w3m-dev 00679] USE_SSL_VERIFY fix. +* [w3m-dev 00686] w3mhelperpanel.c fix. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +* [w3m-dev 00692] EWS4800 support for /usr/abiccs/bin/cc. + +From: Hiroshi Kawashima <kei@arch.sony.co.jp> +* [w3m-dev 00742] mipsel architecture support. + +2000.5.17 +From: Hiroaki Shimotsu <shim@d5.bs1.fc.nec.co.jp> +* [w3m-dev 00543] Bugfix: personal_document_root doesn't work. +* [w3m-dev 00544] When opening file:///dir/, if index.html exists in + that directory, open the file instead of displaying directory list. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00545] w3m -num fix. +* [w3m-dev 00557] Bugfix: When using -dump option, temporary files don't be unlinked. + +From: Okabe Katsuya <okabek@guitar.ocn.ne.jp> +* [w3m-dev 00568] Bugfix: When <blockquote> exists outside of <tr>..</tr> in <table>, + the table corrupts. + +2000.5.16 +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* [w3m-dev 00487] Bugfix: supports terminal without sr capability. +* [w3m-dev 00512][w3m-dev 00514][w3m-dev 00515] Kanji-code decision enhancement. +* [w3m-dev 00530] Bugfix: w3m can't handle cgi using <ISINDEX>. +* [w3m-dev 00537] Remove CR/LF within URL. +* [w3m-dev 00542] Layered frameset support. + +From: SASAKI Takeshi <sasaki@ct.sakura.ne.jp> +* [w3m-dev 00488] id attribute support bugfix. +* [w3m-dev 00497] configure automatically detects IPv6 environment. + +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +* [w3m-dev 00489] + * Bugfix: a page doesn't be displayed which requires SSL client authentication. + * Enhancement: -o option parameter list + * [w3m-dev 00519] Security hole fix about I command. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* [w3m-dev 00498] Filename completion bugfix. +* [w3m-dev 00508] Color display bugfix. +* [w3m-dev 00518] Security hole fix about I command. +* [w3m-dev 00535] GPM/SYSMOUSE support bugfix. + +From: Kazuhiro Nishiyama <nishiyama@mx1.tiki.ne.jp> +* [w3m-dev 00503] $extension correction in Cygwin. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +* [w3m-dev 00516] When transmitting a string to form, it was made not to escape + a safe character. + +From: Tsutomu Okada <okada@furuno.co.jp> +* [w3m-dev 00471] Bugfix: when displaying the page which has a link in the + beginning of the screen, the first link doesn't get active. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +* [w3m-dev 00539] proxy initialization bugfix. + +2000.4.24 +From: aito +* free memory of hidden buffers. (CLEAR_BUF) +* when file:// style local file access fails, don't retry + as http://file://. +* Bugfix: mouse doesn't work when both GPM and SYSMOUSE are undefined. + +From: rubikitch <rubikitch@ruby-lang.org> +* Enhancement: Save Buffer URL into file. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* FTP proxy bugfix. +* C comment cleanup. +* Bugfix: After window resize, reshapeBuffer() is called on + each keyin. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +* when gc library exists under /usr/local, configure decides + found -> dones't seem to work. + +2000.4.21 +From: Kiyokazu SUTO <suto@ks-and-ks.ne.jp> +* Enhancement: When FTP login password ends with @, append the FQDN of + the host. +* When environment variable NNTPMODE is defined, send MODE command + using the value. +* Following options are added. + ssl_verify_server ON/OFF + Do SSL server verification (default OFF) + ssl_cert_file filename + PEM certification file for SSL client (default NULL) + ssl_key_file filename + PEM secret key file for SSL client (default NULL) + ssl_ca_path directory + Path for the directory of PEM certification files (default NULL) + ssl_ca_file file + Filename of PEM certification files + +From: Tsutomu Okada <okada@furuno.co.jp> +* Bugfix: DEL key causes core dump in line input mode. +* Comment processing bugfix. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +* Proxy authentication support for FTP proxy. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* Bugfix: <input_alt fid=0> causes core dump. + +From: aito +* Bugfix: When a table is in a cell with more than one colspan, + the width of the inner table gets wired. +* -model=, -lang= options are added to configure. + +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- All T/NILs are replaced with TRUE/FALSE. +- Messages are added for FTP connection. + +2000.4.7 +From: aito +* Bugfix: <select> without </select> causes core dump. +* Bugfix: Compilation fails unless MOUSE is defined. + +From: Tsutomu Okada <okada@furuno.co.jp> +* Bugfix: Following a link to a local file causes core dump. +* Bugfix: DEL key in line editing causes core dump. + +From: Shin HATTORI <mituzi@he.mirai.ne.jp> +* Bugfix: bzip2 support. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* Bugfix: -dump, -dump_head, -dump_source option interfares + each other. +* Improvement: -o option added. +* Bugfix: -dump option causes core dump. +* Bugfix: mouse operation gets inactive while message is displayed. +* Bugfix: window size change. +* Improvement: Default of quit confirmation is now 'n' +* term.c enhancements + +From: Sven Mascheck <mascheck@faw.uni-ulm.de> +* There are websites using (unprintable) special characters (eg '0x96') + to 'feature' microsoft browsers. At least in the western configuration + (the only one i know), w3m doesn't check if characters are printable, + thus they confuse particularly the /xfree/ xterm (knowing more special + characters than other xterms). + Something like the attached patch could prevent this + (also only affects western version). + Instead of (superfluously) using isprint() with the locale, + it now just checks the range (pointed out by Christian Weisgerber). + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +* C++ style comments are changed into C style. + +2000.4.6 +From: lars brinkhoff <lars@nocrew.org> +ARM linux patch. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Improvement: 'u' command shows form type and action. + +From: patakuti +* Bugfix: -dump option for other than text/html document doesn't work. +* Improvement: Association between extension and mime-type can be + specified by ~/.mime.types. + +2000.4.5 +From: Sakamoto <hsaka@mth.biglobe.ne.jp> +* Bugfix: <Hn>...</Hn> in table makes the table width weird. +* Bugfix: </ol>,</ul>,</dl>,</blockquote> without opening tag + make the table ugly. + +From: "Shin'ya Kumabuchi" <kumabu@t3.rim.or.jp> +* Bugfix: w3m eventually sends Pragma: no-cache header inappropriately. + +From: Tomoyuki Kosimizu <greentea@fa2.so-net.ne.jp> +Bugfix around rc.c + +2000.3.29 +From: Altair <NBG01720@nifty.ne.jp> +OS/2 support improvement. +* Bugfix: w3m fails to open directory. +* Improvement: arrow keys are effective in non-X environment of OS/2 +* Bugfix: Couldn't invoke external program. +* Improvement: Enable drive letter. + +From: David Leonard <leonard@csee.uq.edu.au> +after filling in a simple form + <form action="https://internal.csee.uq.edu.au/cgi-bin/login.cgi" method=POST> +a cookie is received and then w3m dumps core. + +From: Ken Yap <ken@nlc.net.au> +I have made w3m work on DJGPP (protected mode 32-bit programs running +from DOS/Win). The resulting binary after compression is only 220kB, +which means it's possible to give a floppy for a 386 with 4-8 MB memory +for browsing the web! + +From: "SHIROYAMA Takayuki" <psi@stellar.co.jp> +From: Jeroen Scheerder <J.Scheerder@cwi.nl> +MacOS X Server patch. + +2000.2.25 +From: Ambrose Li +I found a bug in <img alt=""> +handling. If alt="" is not suppressed, the line containing the img +element is not wrapped. I have verified that the bug exists in w3m +0.1.6; the bug seems to still exist in w3m 0.1.7, but I have not +finished compiling it. + +From: aito +Bug fix: <option> without <select> causes core dump. +Bugfix: The first line in <blockquote> doesn't indented. +Improvement: application/x-bzip support. +Bugfix: GC fails in mktable, w3mbookmark, w3mhelperpanel. +Bugfix: mouse drag. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bug fix: Illegal tags make w3m's behavior unstable. +quoteShell() security hole fix. +Bug fix: w3m dumps core inside set_environ(). +Bug fix: w3m doesn't do <table width="xxx%"> +Improvement: '!' command doesn't make screen dirty. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Bug fix: Temporary file paths contain //. +Bug fix: 0.1.7 fails with https. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Changes w3m's behavior such that connection failure to the proxy +server causes an error. +Bug fix: URLs specified in the command line don't be involved +into URL history. + +From: sasaki@ct.sakura.ne.jp +HTML4.0 ID attribute support. + +From: Okabe Katsuya <okabe@fphy.hep.okayama-u.ac.jp> +table get weird when it contains <input type=hidden>. +$B=$@5!%(B + +2000.2.12 +From: Rogue Metal - Jake Moorman <roguemtl@stampede.org> +- added GNU-style comments for all #ifdef/#else/#endif + modified: almost all files +- renamed w3mhelp_en and w3mhelp_ja to w3mhelp-w3m_en and w3mhelp-w3m_ja + (to aid in handling of additional keybindings in the future) + modified: XMakefile, XMakefile.dist, config.h, configure, help files +- corrected error in w3mhelp-lynx_en ('Japanese' link was pointing to + Japanese language help file for the w3m keybinding, not the lynx + keybinding) + modified: w3mhelp-lynx_en.html +- replaced 'Loading {URL}' message with more specific messages about + current status ('Performing hostname lookup on {hostname}' and + 'Connecting to {hostname}') + modified: main.c, url.c + +2000.2.10 +From: roguemtl@stampede.org (Jacob Moorman of the [MH] Free Software Group) +- added support for PageUp and PageDown in list boxes + +2000.1.21 +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +1. conn.eventMask is set to 0 which disables reception of all types + of events. Effectively, this disables GPM support altogether. + Probably "~0" was intended, to enable reception of all types of + events. +2. conn.maxMod is set to ~0, which means that events with a modifier + key (shift, control, etc.) set are also sent to w3m. Since w3m + doesn't do anything with these events, they should rather be + passed on to other clients. Changing this to "conn.maxMod = 0" + will for example allow the use of the mouse in w3m *and* mouse + clicks with shift held down for console cut-and-paste. + +From: naddy@mips.rhein-neckar.de (Christian Weisgerber) +I would like to suggest a small change to w3m's GPM support: +Rather than explicitly drawing the mouse pointer, this could be left to +the server, i.e. +- remove GPM_DRAWPOINTER() calls, +- set conn.defaultMask to GPM_MOVE|GPM_HARD. + +From: aito +When '<' is used as other than start of a tag, let the '<' +be displayed. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Bug fix of screen redraw. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +* Accept discard attribute of Set-Cookie2. +* insert a blank line just after </dl>. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +<table> Geometry calculation bugfix. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bugfix of inputLineHist(). + +2000.1.14 +From: ChiDeok Hwang <cdhwang@sr.hei.co.kr> +When I browse http://i.am/orangeland and press 'v' to see document +info, w3m got seg. fault. +Reason was above site had the very strange frameset with only one frame. +<frameset rows="100%,*" ... > +Simple following fix was enough for me. + +From: aito +When no scheme is specified in the URL, w3m tries to open local file, +and when it fails w3m prepends "http://". + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +target="_parent" support +frame-relatex bugfixes. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Screen redraw bugfix. + +2000.1.12 +From: aito +word fill support. (undocumented) +add #define FORMAT_NICE in config.h. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +"w3m ." and w3mhelperpanel doesn't work. + +2000.1.11 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Bugfix of cookie behavior. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +table geometry calculation improvement. + +From: aito +C-c make the external viewer to exit. + +From: <sekita-n@hera.im.uec.ac.jp> +Added an option to suppress sending Referer: field. + +2000.1.4 +From: Sven Oliver Moll <smol0999@rz.uni-hildesheim.de> +There was one thing that's been anoying me, so I got it fixed: the +behaviour of mouse dragging. The push of the mousebutton is +interpreted of dragging the text behind the window. My intuition in +dragging is that I drag the window over the text. So I added a config +option called 'reverse mouse'. + +From: aito +'M' command (external browser) added to Lynx-like keymap. + +From: SUMIKAWA Munechika <sumikawa@ebina.hitachi.co.jp> +IPv6 related bugfix. + +From: kjm@rins.ryukoku.ac.jp (KOJIMA Hajime) +NEWS-OS 6.x support. + +From: aito +configure detects IPv6 support automatically. +(Thanks to sumikawa@ebina.hitachi.co.jp) + +1999.12.28 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of link coloring/underlining. +From: Fumitoshi UKAI <ukai@debian.or.jp> +Now w3m has an option not to render <IMG ALT="">. + +From: aito +Even when HTTP server response code is other than 200, +w3m does user authorization if WWW-Authenticate: header exists. + +From: Yamate Keiichirou <yamate@ebina.hitachi.co.jp> +When following the Location: header, w3m doesn't chop the +LF character off. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Improvements of table rendering algorithm. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Now w3m allows a comment <!-- .... -- > (spaces between -- and >) + +1999.12.27 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Improvement of dirlist.cgi. + +1999.12.14 +From: Christian Weisgerber <naddy@unix-ag.uni-kl.de> +- I have appended a small patch to add support for the Home/End/ + PgUp/PgDn keys at the FreeBSD "syscons" console. + (It would be much preferable if w3m read the key sequences from + the termcap entry rather than having them hardcoded, but this + would require a substantial rewrite.) + +From: aito +* w3m-control: GOTO url support. +* When a document has <meta http-equiv="Refresh" content="0; url=URL"> + tag, the moved document is loaded immediately. +* When invoking an external browser by 'M' or 'ESC M' and the browser + is not defined, w3m prompts to input command to process the URL. + +1999.12.8 +From: aito +Proxy-Authorization support. + +1999.12.3 +From:aito +Now w3m can use an external command (local CGI script) for +directory listing. Default is hsaka's dirlist.cgi. +(in `scripts' directory) + +1999.12.2 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +In menu selection and buffer selection mode, cursor now points +the selected item. (for blind people's convenience) + +From: aito +Now w3m doesn't use GPM library when using +xterm. + +1999.12.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Starting up with environment variable HTTP_HOME +causes hangup. + +From: Fumitoshi UKAI <ukai@debian.or.jp> +Some kind of form causes segmentation fault. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +align attribute support for <tr> tag. +Now default alignment of <th> become CENTER. + +From: Tsutomu Okada <okada@furuno.co.jp> +COOKIES in func.c is changed to COOKIE + +From: aito +Now w3m accepts HTTP headers which have no white space after :. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Serial number of anchors in TABLE gets incorrect. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of -v option. + +1999.11.26 +From: Fumitoshi UKAI <ukai@debian.or.jp> +When arguments in an external command in mailcap +are enclosed by ' ', the command is not executed +correctly. + +1999.11.20 +From: SASAKI Takeshi <sasaki@isoternet.org> +Turning 'Active link color' on causes core dump. + +1999.11.19 +From: aito +Now w3m uses GPM library if available. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Further enhancement of progress bar. + +1999.11.18 +From: Ben Winslow <rain@insane.loonybin.net> +Enhancement of progress bar. + +From: patakuti +If an <input type=button> tag has no `name' attribute, +w3m adds it an inappropriate name attribute. + +From: $B$d$^(B +Now w3m can handle a frameset that has both ROWS and COLS. + +From: aito +Now bookmarking is done by a separate command w3mbookmark. + +C-s $B$G2hLLI=<($,;_$^$C$F$$$?%P%0$N=$@5!%(B + +$BJ8;zF~NO;~$K(B C-g $B$GCf;_$G$-$k$h$&$K$7$?!%(B + +From: hovav@cs.stanford.edu +When downloading a file, an attempt to save it to a non-exist +directory causes core dump. + +From: minoura@netbsd.org +A character entity like ሴ (greater than 0xff) +causes segmentation fault. + +From: Christi Alice Scarborough <christi@chiark.greenend.org.uk> +Active link color patch. + +1999.11.17 +From: aito +Now <OL>,<UL> make blank line before and after the list only if +the list is in the first level. +A bookmark file can be specified by -bookmark option. + +From: Hiroaki Shimotsu <shim@nw.bs1.fc.nec.co.jp> +Now 'N' is bound to 'search previous match'. + +1999.11.16 +From: Kiyohiro Kobayashi <k-kobaya@mxh.mesh.ne.jp> +Enhancement of FTP directory listing. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +checkContentType() Bug fix + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Menu behavior is changed. +* C-f,C-v : display next page +* C-b,M-v : display previous page +* ^[[L (console), ^[[E (pocketBSD) are recognized as INS key. +* DEL : back to parent menu (same as C-h) +* #define MENU_THIN_FRAME -> use thin rules to draw menus + (default is #undef) +* Now one can move to next/previous menu page by clicking + ':' on the bottom/top of the menu. +* Clicking outside the menu causes cancellation of sub-menu. +* <, >, +, - abandoned + +From: $B$*$+$@(B <okada@furuno.co.jp> +Now C-a/C-e are bound to 'jump to the first/last item in menu.' + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +From: Fumitoshi UKAI <ukai@debian.or.jp> +w3m doesn't recognize FTP Multiline reply. + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +Bugfix of buffer selection mode. + +1999.11.15 +From: aito +If a document has <BASE> tag and the base URL is different +from the current URL, w3m sends incorrect Referer: value. +A control character written as &#xnnn; (for example, 
) +is not be decoded correctly. +Now local-CGI scripts have to be located in either file:///cgi-bin/ +or file:///usr/local/lib/w3m/. Scripts in any other directory +are treated as plain texts. +Most system() calls are replaced with mySystem(). + +1999.11.11 +From: aito +Tagname search algorithm is changed from linear search to +hash table. + +1999.11.5 +From: aito +If a table contains character entities &xxx; which represent +latin-1 characters, column width gets incorrect. + +1999.11.1 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +w3m-991028+patch1 doesn't compile when compiled without menu. + +From: ukai@debian.or.jp +A bugfix of Strcat_char(). + +1999.10.28 +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +When accessing + file?var=value/#label +#label doesn't be regarded as a label. + +From: aito +991027 version contains debug code. + +1999.10.27 +From: OKADA <okada@furuno.co.jp> +When JP_CHARSET is defined, Latin-1 character like ¢ isn't +displayed. + +From: Yama +A cookie without path haven't been handled correctly. + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +When reloadint CGI page with POST method, w3m tries to load it +with GET method. + +From: aito +When following a link from a page in frame, Referer: value +is not a URL of the page but the URL of fremeset page. + +configure is slightly changed. User can now choose model. +-yes option added. + +FTP failed when a server returns other than 150 as a result code +of RETR/NLST command. + +<select multiple>...</select> doesn't work correctly. + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +In getshell, getpipe, execdict functions, +buffer name is changed to include command name. + +From: Colin Phipps <cph@crp22.trin.cam.ac.uk> +When a load of cookies expire w3m SEGVs on startup. + +From: pmaydell@chiark.greenend.org.uk +I was looking through the w3m source, and noticed that it defines the +following macro: +#define IS_SPACE(x) (isspace(x) && !(x&0x80)) +Now this won't work as expected if x is an expression with side effects +(it will be evaluated twice). A quick grep of the sources reveals +several places where the macro is used like this: +file.c: if (IS_SPACE(*++p)) +which is almost certainly a bug... (although I haven't tried to actually +work out what the effects of it would be). + +1999.10.21 +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Bug fix of buffername of source/HTML display. + +1999.10.20 + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +When <P> exists between <dt> and <dd> or <h3> and </h3>, +all text after that point becomd bold. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Now 'B' command backs to previous page when viewing frame page. +'F','v','=' command toggle. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Inappropriate behaviours with -dump option have been fixed. +* w3m -dump < file appends \377 before EOF. +* w3m -dump -s < file doesn't convert character code. + -num, -S don't take effect. +* w3m -dump -T text/plain < file outputs nothing. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* menu.c: graphic char related bugfixes. +* terms.c: When Cygwin, T_as = T_as = T_ac = "" + Added T_ac != '\0' in graph_ok() +* bookmark.c: KANJI_SYMBOL -> LANG == JA + +1999.10.15 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> + 1. Some part does case-sensitive comparison for cookie name. + 2. When executing sleep_till_anykey(), terminal state doesn't + recover. + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +When running configure, it adds multiple -lxxx when +both libxxx.a and libxxx.so exist. [fixed] + +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +* When generating HTML within w3m, those source didn't be quoted. +* Makes it interpret <base href=... target=...> of each source + of a frame. + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +From: hsaka@mth.biglobe.ne.jp (Hironori Sakamoto) +Enhancements of buffer handling functions. + +From: SASAKI Takeshi <sasaki@isoternet.org> +When multiple cookies are sent whose names are different +but domains and paths are identical, older one doesn't +removed. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Cookie comparison must be case insensitive. + +From: aito +* When no ~/.w3m/cookie exists, C-k sometimes cause core dump. +* ~/.w3m/cookie isn't updated when -dump option is used. +* Latin-1 character written as &xxx; isn't regarded as a + roman character. + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Improvements: + o After executing "w3m http://foo.co.jp/foo.jpg", + w3m waits with prompt "Hit any key to quit w3m:". + o "w3m http://foo.co.jp/foo.tar.gz" doesn't display + usage. + o Progress bar is displayed while downloading via ftp. + o FTP download can be interrupted. + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Now w3m can access using SSL via proxy server. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +<form enctype="multipart/form-data"> <input type=file> + +From: "OMAE, jun" <jun-o@osb.att.ne.jp> +w3m-991008 on cygwin causes following problems. + 1. w3m / doesn't display directory list. + 2. When referring local directory, URL becomes like + file://///foo. + 3. Can't load file:///windows. + +From: Fumitoshi UKAI <ukai@debian.or.jp> + % http_proxy=http://foo/bar w3m http: +causes segmentation fault. + +1999.10.8 +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Changed to treat documents that contain designation sequences +to JIS-X0208-1983 and JIS-X0208-1976. + +From: aito +When there is tag sequence like <pre>..<p>..</pre><p> , words +after the last <p> are not folded. [fixed] +Type of counters for number of anchor in a document are changed from +short to int. +Description like `<b><u>hoge</u></b> moge' makes the space between hoge +and mode underlined.[fixed] + +1999.10.7 +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +Cookie support. +SSL support. Still experimental. + +From: aito +Considering those systems who have no static library, now configure +searches lib*.so as well as lib*.a. + +From: HIROSE Masaaki <hirose31@t3.rim.or.jp> +From: Anthony Baxter <anthony@ekorp.com> +`Host:' header lacks port number. [fixed] + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +When moving to a label, URL doesn't change. [fixed] + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Can't handle tags like <ul> nested more than 20 levels.[fixed] + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +Bugfix about Content-Transfer-Encoding: quoted-printable + +From:aito +When <frameset > contains both COLS= and ROWS=, w3m can't render +that frame correctly. [fixed] + +From: sakane@d4.bsd.nes.nec.co.jp (Yoshinobu Sakane) +Bugfixes of w3m-990928 gziped file support. + +From: Hironori Sakamoto <h-saka@lsi.nec.co.jp> +* Bug fix of decoding B-encoded file. + +1999.9.28 + +From: SASAKI Takeshi <sasaki@isoternet.org> +wrap search mode is added. +&#xnnnn; character entity representation support. + +From: aito +Change default character color to 'terminal' (do nothing). + +From: Okabe Katsuya <okabe@okaibm.hep.okayama-u.ac.jp> +When using BG_COLOR defined w3m on linux console, w3m doesn't refresh +screen on termination. [fixed] + +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +Extra newline is inserted after <pre> tag in frame. [fixed] + +From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> +From: Hironori Sakamoto <hsaka@mth.biglobe.ne.jp> +When opening popup menu by mouse right click, let cursor move to +the clicked position. + +1999.9.16 + +From: aito +Fix a bug that renders <...> in form button as <...> tag. + +From: Iimura uirou@din.or.jp +w3m causes SIGSEGV when DICT is defined in config.h. [fixed] +Added a function to make a link to the background image. + +From: Doug Kaufman <dkaufman@rahul.net> +I just downloaded and compiled the 19990902 version of w3m with cygwin +(b20 with 19990115 cygwin1.dll). The following patch takes care of the +compilation problems. + +From: Hayase +There are undefined variables when compiled on NEXTSTEP 3.3J. [fixed] + +From: Oki +When using <HR> tag in list environment, let horizontal rule begin +from indented position. + +From: Okada +w3m gets segmentation fault when rendering character entity like ア. +[fixed] + +From: Sakamoto hsaka@mth.biglobe.ne.jp +Many bugfixes for local CGI. + +From: Katsuya Okabe okabe@okaibm.hep.okayama-u.ac.jp +Graphics characters go bad when using on linux console. [fixed] +Bug fixes on color display on kterm. [fixed] +Bug fix on table renderer. [fixed] + +From: Sakamoto hsaka@mth.biglobe.ne.jp +Download from page in frame doesn't work correctly. [fixed] + +1999.9.3 +From: Sakamoto hsaka@mth.biglobe.ne.jp +Bug fixes of URL analyzer. + +From: Katsuya Okabe okabe@okaibm.hep.okayama-u.ac.jp +Bugfix of screen redraw routine. +-- +Akinori Ito +tel&fax: 0238-26-3369 +E-mail: aito@eie.yz.yamagata-u.ac.jp + diff --git a/doc/keymap.default b/doc/keymap.default new file mode 100644 index 0000000..38279ce --- /dev/null +++ b/doc/keymap.default @@ -0,0 +1,115 @@ +# A sample of ~/.w3m/keymap (default) +# +# Ctrl : C-, ^ +# Escape: ESC-, M-, ^[ +# Space : SPC, ' ' +# Tab : TAB, ^i, ^I +# Delete: DEL, ^? +# Up : UP, ^[[A +# Down : DOWN, ^[[B +# Right : RIGHT, ^[[C +# Left : LEFT, ^[[D + +keymap C-@ MARK +keymap C-a LINE_BEGIN +keymap C-b MOVE_LEFT +keymap C-e LINE_END +keymap C-f MOVE_RIGHT +keymap C-g LINE_INFO +keymap C-h HISTORY +keymap TAB NEXT_LINK +keymap C-j GOTO_LINK +keymap C-k COOKIE +keymap C-l REDRAW +keymap C-m GOTO_LINK +keymap C-n MOVE_DOWN +keymap C-p MOVE_UP +keymap C-r SEARCH_BACK +keymap C-s SEARCH +keymap C-v NEXT_PAGE +keymap C-z SUSPEND + +keymap SPC NEXT_PAGE +keymap ! SHELL +keymap \" REG_MARK +keymap # PIPE_SHELL +keymap $ LINE_END +keymap , LEFT +keymap . RIGHT +keymap / SEARCH +keymap : MARK_URL +keymap < SHIFT_LEFT +keymap = INFO +keymap > SHIFT_RIGHT +keymap ? SEARCH_BACK +keymap @ READ_SHELL +keymap B BACK +keymap E EDIT +keymap F FRAME +keymap G END +keymap H HELP +keymap I VIEW_IMAGE +keymap J UP +keymap K DOWN +keymap M EXTERN +keymap Q EXIT +keymap R RELOAD +keymap S SAVE_SCREEN +keymap U GOTO +keymap V LOAD +keymap W PREV_WORD +keymap Z CENTER_H +keymap \^ LINE_BEGIN +keymap a SAVE_LINK +keymap b PREV_PAGE +keymap c PEEK +keymap g BEGIN +keymap h MOVE_LEFT +keymap i PEEK_IMG +keymap j MOVE_DOWN +keymap k MOVE_UP +keymap l MOVE_RIGHT +keymap n SEARCH_NEXT +keymap o OPTIONS +keymap q QUIT +keymap s SELECT +keymap u PEEK_LINK +keymap v VIEW +keymap w NEXT_WORD +keymap z CENTER_V + +keymap M-TAB PREV_LINK +keymap M-C-j SAVE_LINK +keymap M-C-m SAVE_LINK + +keymap M-: MARK_MID +keymap M-< BEGIN +keymap M-> END +keymap M-I SAVE_IMAGE +keymap M-M EXTERN_LINK +keymap M-W DICT_WORD_AT +keymap M-a ADD_BOOKMARK +keymap M-b BOOKMARK +keymap M-e EDIT_SCREEN +keymap M-g GOTO_LINE +keymap M-n NEXT_MARK +keymap M-p PREV_MARK +keymap M-s SAVE +keymap M-v PREV_PAGE +keymap M-w DICT_WORD + +keymap UP MOVE_UP +keymap DOWN MOVE_DOWN +keymap RIGHT MOVE_RIGHT +keymap LEFT MOVE_LEFT + +keymap M-[E MENU +keymap M-[L MENU + +keymap M-[1~ BEGIN +keymap M-[2~ MENU +keymap M-[4~ END +keymap M-[5~ PREV_PAGE +keymap M-[6~ NEXT_PAGE +keymap M-[28~ MENU + diff --git a/doc/keymap.lynx b/doc/keymap.lynx new file mode 100644 index 0000000..6c14a30 --- /dev/null +++ b/doc/keymap.lynx @@ -0,0 +1,109 @@ +# A sample of ~/.w3m/keymap (lynx-like) +# +# Ctrl : C-, ^ +# Escape: ESC-, M-, ^[ +# Space : SPC, ' ' +# Tab : TAB, ^i, ^I +# Delete: DEL, ^? +# Up : UP, ^[[A +# Down : DOWN, ^[[B +# Right : RIGHT, ^[[C +# Left : LEFT, ^[[D + +keymap C-@ MARK +keymap C-a BEGIN +keymap C-b PREV_PAGE +keymap C-e END +keymap C-f NEXT_PAGE +keymap C-h HISTORY +keymap TAB NEXT_LINK +keymap C-j GOTO_LINK +keymap C-k COOKIE +keymap C-l REDRAW +keymap C-m GOTO_LINK +keymap C-n NEXT_LINK +keymap C-p PREV_LINK +keymap C-r RELOAD +keymap C-s SEARCH +keymap C-v NEXT_PAGE +keymap C-w REDRAW +keymap C-z SUSPEND + +keymap SPC NEXT_PAGE +keymap ! SHELL +keymap \" REG_MARK +keymap # PIPE_SHELL +keymap $ LINE_END +keymap + NEXT_PAGE +keymap - PREV_PAGE +keymap / SEARCH +keymap : MARK_URL +keymap < SHIFT_LEFT +keymap = INFO +keymap > SHIFT_RIGHT +keymap ? HELP +keymap @ READ_SHELL +keymap B BACK +keymap E EDIT +keymap F FRAME +keymap G GOTO_LINE +keymap H HELP +keymap I VIEW_IMAGE +keymap J UP +keymap K DOWN +keymap N NEXT_MARK +keymap P PREV_MARK +keymap Q EXIT +keymap R RELOAD +keymap S SAVE_SCREEN +keymap U GOTO +keymap V LOAD +keymap Z CENTER_H +keymap \\ SOURCE +keymap \^ LINE_BEGIN +keymap a ADD_BOOKMARK +keymap b PREV_PAGE +keymap c PEEK +keymap d SAVE +keymap g GOTO +keymap h MOVE_LEFT +keymap i PEEK_IMG +keymap j MOVE_DOWN +keymap k MOVE_UP +keymap l MOVE_RIGHT +keymap n SEARCH_NEXT +keymap o OPTIONS +keymap p SAVE_SCREEN +keymap q QUIT +keymap s SELECT +keymap u PEEK_LINK +keymap v BOOKMARK +keymap z CENTER_V + +keymap M-TAB PREV_LINK +keymap M-C-j SAVE_LINK +keymap M-C-m SAVE_LINK + +keymap M-: MARK_MID +keymap M-I SAVE_IMAGE +keymap M-a ADD_BOOKMARK +keymap M-b BOOKMARK +keymap M-e EDIT_SCREEN +keymap M-s SAVE +keymap M-v PREV_PAGE + +keymap UP PREV_LINK +keymap DOWN NEXT_LINK +keymap RIGHT GOTO_LINK +keymap LEFT BACK + +keymap M-[E MENU +keymap M-[L MENU + +keymap M-[1~ BEGIN +keymap M-[2~ MENU +keymap M-[4~ END +keymap M-[5~ PREV_PAGE +keymap M-[6~ NEXT_PAGE +keymap M-[28~ MENU + diff --git a/doc/menu.default b/doc/menu.default new file mode 100644 index 0000000..3e45d1c --- /dev/null +++ b/doc/menu.default @@ -0,0 +1,33 @@ +# A sample of ~/.w3m/menu (default) +# +# menu MENU_ID +# func LABEL FUNCTION KEYS +# popup LABEL MENU_ID KEYS +# nop LABEL +# end +# +# MENU_ID +# Main: Main menu +# Select: Buffer selection menu + +menu Main + func " Back (b) " BACK "b" + func " Select Buffer(s) " SELECT "s" + func " View Source (v) " VIEW "vV" + func " Edit Source (e) " EDIT "eE" + func " Save Source (S) " SAVE "S" + func " Reload (r) " RELOAD "rR" + nop " -----------------" + + func " Go Link (a) " GOTO_LINK "a" + func " Save Link (A) " SAVE_LINK "A" + func " View Image (i) " VIEW_IMAGE "i" + func " Save Image (I) " SAVE_IMAGE "I" + func " View Frame (f) " FRAME "fF" + nop " ---------------- " + func " Bookmark (B) " BOOKMARK "B" + func " Help (h) " HELP "hH" + func " Option (o) " OPTIONS "oO" + nop " ---------------- " + func " Quit (q) " QUIT "qQ" +end diff --git a/doc/menu.submenu b/doc/menu.submenu new file mode 100644 index 0000000..e55193d --- /dev/null +++ b/doc/menu.submenu @@ -0,0 +1,44 @@ +# A sample of ~/.w3m/menu (submenu type) +# +# menu MENU_ID +# func LABEL FUNCTION KEYS +# popup LABEL MENU_ID KEYS +# nop LABEL +# end +# +# MENU_ID +# Main: Main Menu +# Select: Buffer selection menu + +menu Main + func "Back (b)" BACK "b" + popup "Buffer ops >(f)" Buffer "fF" + popup "Link ops >(l)" Link "lL" + nop "----------------" + popup "Bookmarks >(B)" Bookmark "B" + func "Help (h)" HELP "hH" + func "Options (o)" OPTIONS "oO" + nop "----------------" + func "Quit (q)" QUIT "qQ" +end + +menu Buffer + popup "Buffer select(s)" Select "s" + func "View source (v)" VIEW "vV" + func "Edit source (e)" EDIT "eE" + func "Save source (S)" SAVE "S" + func "Reload (r)" RELOAD "rR" +end + +menu Link + func "Go link (a)" GOTO_LINK "a" + func "Save link (A)" SAVE_LINK "A" + func "View image (i)" VIEW_IMAGE "i" + func "Save image (I)" SAVE_IMAGE "I" + func "View frame (f)" FRAME "fF" +end + +menu Bookmark + func "Read bookmark (b)" BOOKMARK "bB" + func "Add page to bookmark(a)" ADD_BOOKMARK "aA" +end diff --git a/doc/w3m.1 b/doc/w3m.1 new file mode 100644 index 0000000..91ae2a4 --- /dev/null +++ b/doc/w3m.1 @@ -0,0 +1,134 @@ +.nr N -1 +.nr D 5 +.TH W3M 1 Local +.UC 4 +.SH NAME +w3m \- a text based Web browser and pager +.SH SYNOPSIS +.B w3m +[options] [URL or filename] +.PP +Use "w3m -h" to display a complete list of current options. +.SH DESCRIPTION +.\" This defines appropriate quote strings for nroff and troff +.ds lq \&" +.ds rq \&" +.if t .ds lq `` +.if t .ds rq '' +.\" Just in case these number registers aren't set yet... +.if \nN==0 .nr N 10 +.if \nD==0 .nr D 5 +.I +w3m +is a World Wide Web (WWW) text based client. It has English and +Japanese help files and an option menu and can be configured to +use either language. It will display hypertext markup language +(HTML) documents containing links to files residing on the local +system, as well as files residing on remote systems. It can +display HTML tables and frames. +In addition, it can be used as a "pager" in much the same manner +as "more" or "less". +Current versions of +.I +w3m +run on +Unix (Solaris, SunOS, HP-UX, Linux, FreeBSD, and EWS4800) +and on +Microsoft Windows 9x/NT. +.PP +.SH OPTIONS +At start up, \fIw3m\fR will load any local +file or remote URL specified at the command +line. For help with runtime options, press \fB"H"\fR +while running \fIw3m\fR. +Command line options are: +.PP +.TP +.B -t tab +set tab width +.TP +.B -r +ignore backspace effect +.TP +.B -l line +# of preserved line (default 10000) +.TP +.B -s +Shift_JIS +.TP +.B -j +JIS +.TP +.B -e +EUC-JP +.TP +.B -B +load bookmark +.TP +.B -T type +specify content-type +.TP +.B -m +internet message mode +.TP +.B -M +monochrome display +.TP +.B -F +automatically render frame +.TP +.B -dump +dump formatted page into stdout +.TP +.B -cols width +specify column width (used with -dump) +.TP +.B -ppc count +specify the number of pixels per character (default 8.0) +Larger values will make tables narrower. +.TP +.B -dump_source +dump page source into stdout +.TP +.B +<num> +goto <num> line +.TP +.B -debug +DO NOT USE +.SH EXAMPLES +.TP +To use w3m as a pager: +.br +$ ls | w3m +.br +.TP +To use w3m to translate HTML files: +.br +$ cat foo.html | w3m -T text/html +.TP +or +.br +$ cat foo.html | w3m -dump -T text/html >foo.txt +.SH NOTES +This is the +.I +w3m +990604 Release. +.PP +Additional information about +.I +w3m +may be found on its Japanese language Web site located at: + http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/ +.br +or on its English version of the site at: + http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/ +.SH ACKNOWLEDGMENTS +.I +w3m +has incorporated code from several sources. +Hans J. Boehm, Alan J. Demers, Xerox Corp. and Silicon Graphics +have the copyright of the GC library comes with w3m package. +Users have contributed patches and suggestions over time. +.SH AUTHOR +Akinori ITO <aito@ei5sun.yz.yamagata-u.ac.jp> @@ -0,0 +1,1493 @@ +/* $Id: etc.c,v 1.1 2001/11/08 05:14:33 a-ito Exp $ */ +#include "fm.h" +#include <pwd.h> +#include "myctype.h" +#include "html.h" +#include "local.h" +#include "hash.h" +#include "terms.h" + +#ifdef GETCWD +#include <unistd.h> +#include <sys/param.h> +#endif /* GETCWD */ + +#include <sys/types.h> +#include <time.h> +#include <sys/wait.h> + +#ifdef __WATT32__ +#define read(a,b,c) read_s(a,b,c) +#define close(x) close_s(x) +#endif /* __WATT32__ */ + +#ifndef STRCASECMP +int +strcasecmp(char *s1, char *s2) +{ + int x; + while (*s1) { + x = tolower(*s1) - tolower(*s2); + if (x != 0) + break; + s1++; + s2++; + } + if (x != 0) + return x; + return -tolower(*s2); +} + +int +strncasecmp(char *s1, char *s2, int n) +{ + int x; + while (*s1 && n) { + x = tolower(*s1) - tolower(*s2); + if (x != 0) + break; + s1++; + s2++; + n--; + } + if (x != 0) + return x; + return 0; +} +#endif /* not STRCASECMP */ + +int +arg_is(char *str, char *tag) +{ + while (*tag) { + if (tolower(*tag) != tolower(*str)) + return 0; + tag++; + str++; + } + while (*str && (*str == ' ' || *str == '\t')) + str++; + return (*str == '='); +} + +int +columnSkip(Buffer * buf, int offset) +{ + int i, maxColumn; + int column = buf->currentColumn + offset; + int nlines = LASTLINE + 1; + Line *l; + + maxColumn = 0; + for (i = 0, l = buf->topLine; + i < nlines && l != NULL; + i++, l = l->next) { + if (l->width < 0) + l->width = COLPOS(l, l->len); + if (l->width - 1 > maxColumn) + maxColumn = l->width - 1; + } + maxColumn -= COLS - 1; + if (column < maxColumn) + maxColumn = column; + if (maxColumn < 0) + maxColumn = 0; + + if (buf->currentColumn == maxColumn) + return 0; + buf->currentColumn = maxColumn; + return 1; +} + +int +columnPos(Line * line, int column) +{ + int i; + + for (i = 1; i < line->len; i++) { + if (COLPOS(line, i) > column) { +#ifdef JP_CHARSET + if (CharType(line->propBuf[i - 1]) == PC_KANJI2) + return i - 2; +#endif + return i - 1; + } + } + return i - 1; +} + +Line * +lineSkip(Buffer * buf, Line * line, int offset, int last) +{ + int i; + Line *l; + + l = currentLineSkip(buf, line, offset, last); + for (i = (LASTLINE - 1) - (buf->lastLine->linenumber - l->linenumber); + i > 0 && l->prev != NULL; + i--, l = l->prev); + return l; +} + +Line * +currentLineSkip(Buffer * buf, Line * line, int offset, int last) +{ + int i, n; + Line *l = line; + + if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) { + n = line->linenumber + offset + LASTLINE; + if (buf->lastLine->linenumber < n) + getNextPage(buf, n - buf->lastLine->linenumber); + while ((last || (buf->lastLine->linenumber < n)) && + (getNextPage(buf, 1) != NULL)); + if (last) + l = buf->lastLine; + } + + if (offset == 0) + return l; + if (offset > 0) + for (i = 0; i < offset && l->next != NULL; i++, l = l->next); + else + for (i = 0; i < -offset && l->prev != NULL; i++, l = l->prev); + return l; +} + +#define MAX_CMD_LEN 128 + +static int +get_cmd(Hash_si * hash, + char **s, + char *args, + char termchar, + int defaultcmd, + int allow_space, + int *status) +{ + char cmdstr[MAX_CMD_LEN]; + char *p = cmdstr; + char *save = *s; + int cmd; + if (status) + *status = 0; + (*s)++; + /* first character */ + if (IS_ALNUM(**s) || **s == '_' || **s == '/') + *(p++) = tolower(*((*s)++)); + else + return defaultcmd; + if (p[-1] == '/') + SKIP_BLANKS(*s); + while ((IS_ALNUM(**s) || **s == '_') && + p - cmdstr < MAX_CMD_LEN) { + *(p++) = tolower(*((*s)++)); + } + if (p - cmdstr == MAX_CMD_LEN) { + /* buffer overflow: perhaps caused by bad HTML source */ + *s = save + 1; + if (status) + *status = -1; + return defaultcmd; + } + *p = '\0'; + + /* hash search */ + cmd = getHash_si(hash, cmdstr, defaultcmd); + if (args != NULL) { + p = args; + while (**s == ' ' || **s == '\t') + (*s)++; + while (**s && **s != '\n' && **s != '\r' && **s != termchar) { + *(p++) = *((*s)++); + } + *p = '\0'; + } + else if (allow_space) { + while (**s && **s != termchar) + (*s)++; + } + if (**s == termchar) + (*s)++; + else if (status) + *status = -1; + return cmd; +} + +int +gethtmlcmd(char **s, int *status) +{ + extern Hash_si tagtable; + return get_cmd(&tagtable, s, NULL, '>', HTML_UNKNOWN, TRUE, status); +} + +static char * +searchAnchorArg(char **arg) +{ + char *p; + if (**arg == '"') { + (*arg)++; + p = *arg; + while (*p && *p != '"') + p++; + } + else { + p = *arg; + while (*p && *p != '>' && *p != ' ' && *p != ',' && + *p != '\t' && *p != '\n') + p++; + } + return p; +} + +char * +getAnchor(char *arg, char **arg_return) +{ + char *p; + char buf[LINELEN]; + + if (arg_is(arg, "name")) { + arg += 4; + while (*arg && (*arg == ' ' || *arg == '\t')) + arg++; + if (*arg != '=') + return NULL; + arg++; + while (*arg && (*arg == ' ' || *arg == '\t')) + arg++; + p = searchAnchorArg(&arg); + buf[0] = '#'; + strncpy(&buf[1], arg, p - arg); + if (arg_return) + *arg_return = p; + return allocStr(buf, p - arg + 1); + } + while (*arg && *arg != '=') + arg++; + if (*arg == '\0') + return NULL; + arg++; + while (*arg && (*arg == ' ' || *arg == '\t')) + arg++; + p = searchAnchorArg(&arg); + if (arg_return) + *arg_return = p; + if (p == arg) + return allocStr(" ", 1); + return allocStr(arg, p - arg); +} + +#ifdef ANSI_COLOR +static int +parse_ansi_color(char **str, Lineprop *effect, Linecolor *color) +{ + char *p = *str, *q; + Lineprop e = *effect; + Linecolor c = *color; + int i; + + if (*p != ESC_CODE || *(p+1) != '[') + return 0; + p += 2; + for (q = p; IS_DIGIT(*q) || *q == ';'; q++) + ; + if (*q != 'm') + return 0; + *str = q + 1; + while (1) { + if (*p == 'm') { + e = PE_NORMAL; + c = 0; + break; + } + if (IS_DIGIT(*p)) { + q = p; + for (p++; IS_DIGIT(*p); p++) + ; + i = atoi(allocStr(q, p - q)); + switch (i) { + case 0: + e = PE_NORMAL; + c = 0; + break; + case 1: + case 5: + e = PE_BOLD; + break; + case 4: + e = PE_UNDER; + break; + case 7: + e = PE_STAND; + break; + case 100: /* for EWS4800 kterm */ + c = 0; + break; + case 39: + c &= 0xf0; + break; + case 49: + c &= 0x0f; + break; + default: + if (i >= 30 && i <= 37) + c = (c & 0xf0) | (i - 30) | 0x08; + else if (i >= 40 && i <= 47) + c = (c & 0x0f) | ((i - 40) << 4) | 0x80; + break; + } + if (*p == 'm') + break; + } else { + e = PE_NORMAL; + c = 0; + break; + } + p++; /* *p == ';' */ + } + *effect = e; + *color = c; + return 1; +} +#endif + +/* + * Check character type + */ + +Str +checkType(Str s, Lineprop * oprop, +#ifdef ANSI_COLOR + Linecolor * ocolor, int * check_color, +#endif + int len) +{ + Lineprop mode; + Lineprop effect = PE_NORMAL; + Lineprop *prop = oprop; + char *str = s->ptr, *endp = &s->ptr[s->length], *bs = NULL; +#ifdef ANSI_COLOR + Lineprop ceffect = PE_NORMAL; + Linecolor cmode = 0; + Linecolor *color = NULL; + char *es = NULL; +#endif + int do_copy = FALSE; + int size = (len < s->length) ? len : s->length; + +#ifdef ANSI_COLOR + if (check_color) + *check_color = FALSE; +#endif + if (ShowEffect) { + bs = memchr(str, '\b', s->length); +#ifdef ANSI_COLOR + if (ocolor) { + es = memchr(str, ESC_CODE, s->length); + if (es) + color = ocolor; + } +#endif + if (s->length > size || (bs != NULL) +#ifdef ANSI_COLOR + || (es != NULL) +#endif + ) { + s = Strnew_size(size); + do_copy = TRUE; + } + } + + while (str < endp) { + if (prop - oprop >= len) + break; + if (bs != NULL) { + if (str == bs - 2 && !strncmp(str, "__\b\b", 4)) { + str += 4; + effect = PE_UNDER; + if (str < endp) + bs = memchr(str, '\b', endp - str); + continue; + } + else if (str == bs - 1 && *str == '_') { + str += 2; + effect = PE_UNDER; + if (str < endp) + bs = memchr(str, '\b', endp - str); + continue; + } + else if (str == bs) { + if (*(str + 1) == '_') { +#ifdef JP_CHARSET + if (s->length > 1 && CharType(*(prop - 2)) == PC_KANJI1) { + str += 2; + *(prop - 1) |= PE_UNDER; + *(prop - 2) |= PE_UNDER; + } + else +#endif /* JP_CHARSET */ + if (s->length > 0) { + str += 2; + *(prop - 1) |= PE_UNDER; + } + else { + str++; + } + } + else if (!strncmp(str + 1, "\b__", 3)) { +#ifdef JP_CHARSET + if (s->length > 1 && CharType(*(prop - 2)) == PC_KANJI1) { + str += 4; + *(prop - 1) |= PE_UNDER; + *(prop - 2) |= PE_UNDER; + } + else +#endif /* JP_CHARSET */ + if (s->length > 0) { + str += 3; + *(prop - 1) |= PE_UNDER; + } + else { + str += 2; + } + } + else if (*(str + 1) == '\b') { +#ifdef JP_CHARSET + if (s->length > 1 && CharType(*(prop - 2)) == PC_KANJI1) { + if (str + 4 <= endp && + !strncmp(str - 2, str + 2, 2)) { + *(prop - 1) |= PE_BOLD; + *(prop - 2) |= PE_BOLD; + str += 4; + } + else { + Strshrink(s, 2); + prop -= 2; + str += 2; + } + } + else +#endif /* JP_CHARSET */ + if (s->length > 0) { + if (str + 3 <= endp && + *(str - 1) == *(str + 2)) { + *(prop - 1) |= PE_BOLD; + str += 3; + } + else { + Strshrink(s, 1); + prop--; + str += 2; + } + } + else { + str += 2; + } + } + else { +#ifdef JP_CHARSET + if (s->length > 1 && CharType(*(prop - 2)) == PC_KANJI1) { + if (str + 3 <= endp && + !strncmp(str - 2, str + 1, 2)) { + *(prop - 1) |= PE_BOLD; + *(prop - 2) |= PE_BOLD; + str += 3; + } + else { + Strshrink(s, 2); + prop -= 2; + str++; + } + } + else +#endif /* JP_CHARSET */ + if (s->length > 0) { + if (str + 2 <= endp && + *(str - 1) == *(str + 1)) { + *(prop - 1) |= PE_BOLD; + str += 2; + } + else { + Strshrink(s, 1); + prop--; + str++; + } + } + else { + str++; + } + } + if (str < endp) + bs = memchr(str, '\b', endp - str); + continue; + } +#ifdef ANSI_COLOR + else if (str > bs) + bs = memchr(str, '\b', endp - str); +#endif + } +#ifdef ANSI_COLOR + if (es != NULL) { + if (str == es) { + int ok = parse_ansi_color(&str, &ceffect, &cmode); + if (str < endp) + es = memchr(str, ESC_CODE, endp - str); + if (ok) { + if (cmode) + *check_color = TRUE; + continue; + } + } + else if (str > es) + es = memchr(str, ESC_CODE, endp - str); + } +#endif + + mode = get_mctype(str); +#ifdef ANSI_COLOR + effect |= ceffect; +#endif +#ifdef JP_CHARSET + if (mode == PC_KANJI) { + prop[0] = (effect | PC_KANJI1); + prop[1] = (effect | PC_KANJI2); + if (do_copy) { + Strcat_char(s, str[0]); + Strcat_char(s, str[1]); + } + prop += 2; + str += 2; +#ifdef ANSI_COLOR + if (color) { + color[0] = cmode; + color[1] = cmode; + color += 2; + } +#endif + } + else +#endif /* JP_CHARSET */ + { + *prop = (effect | mode); + if (do_copy) + Strcat_char(s, *str); + prop++; + str++; +#ifdef ANSI_COLOR + if (color) { + *color = cmode; + color++; + } +#endif + } + effect = PE_NORMAL; + } + return s; +} + +int +calcPosition(char *l, Lineprop *pr, int len, int pos, int bpos, int mode) +{ + static short realColumn[LINELEN + 1]; + static char *prevl = NULL; + int i, j; + + if (l == NULL || len == 0) + return bpos; + if (l == prevl && mode == CP_AUTO) { + if (pos <= len) + return realColumn[pos]; + } + prevl = l; + j = bpos; + for (i = 0;; i++) { + realColumn[i] = j; + if (i == len) + break; + if (l[i] == '\t' && pr[i] == PC_CTRL) + j = (j + Tabstop) / Tabstop * Tabstop; +#ifndef KANJI_SYMBOLS + else if (pr[i] & PC_RULE) + j++; +#endif + else if (IS_UNPRINTABLE_ASCII(l[i], pr[i])) + j = j + 4; + else if (IS_UNPRINTABLE_CONTROL(l[i], pr[i])) + j = j + 2; + else + j++; + } + if (pos >= i) + return j; + return realColumn[pos]; +} + +char * +lastFileName(char *path) +{ + char *p, *q; + + p = q = path; + while (*p != '\0') { + if (*p == '/') + q = p + 1; + p++; + } + + return allocStr(q, 0); +} + +#ifdef NOBCOPY +void +bcopy(void *src, void *dest, int len) +{ + int i; + if (src == dest) + return; + if (src < dest) { + for (i = len - 1; i >= 0; i--) + dest[i] = src[i]; + } + else { /* src > dest */ + for (i = 0; i < len; i++) + dest[i] = src[i]; + } +} + +void +bzero(void *ptr, int len) +{ + int i; + for (i = 0; i < len; i++) + *(ptr++) = 0; +} +#endif /* NOBCOPY */ + +char * +mybasename(char *s) +{ + char *p = s; + while (*p) + p++; + while (s <= p && *p != '/') + p--; + if (*p == '/') + p++; + else + p = s; + return allocStr(p, 0); +} + +char * +mydirname(char *s) +{ + char *p = s; + while (*p) + p++; + if (s != p) + p--; + while (s != p && *p == '/') + p--; + while (s != p && *p != '/') + p--; + if (*p != '/') + return "."; + while (s != p && *p == '/') + p--; + return allocStr(s, strlen(s) - strlen(p) + 1); +} + +#ifndef STRERROR +char * +strerror(int errno) +{ + extern char *sys_errlist[]; + return sys_errlist[errno]; +} +#endif /* not STRERROR */ + +#ifndef SYS_ERRLIST +char **sys_errlist; + +prepare_sys_errlist() +{ + int i, n; + + i = 1; + while (strerror(i) != NULL) + i++; + n = i; + sys_errlist = New_N(char *, n); + sys_errlist[0] = ""; + for (i = 1; i < n; i++) + sys_errlist[i] = strerror(i); +} +#endif /* not SYS_ERRLIST */ + +int +next_status(char c, int *status) +{ + switch (*status) { + case R_ST_NORMAL: + if (c == '<') { + *status = R_ST_TAG0; + return 0; + } + else if (c == '&') { + *status = R_ST_AMP; + return 1; + } + else + return 1; + break; + case R_ST_TAG0: + if (c == '!') { + *status = R_ST_CMNT1; + return 0; + } + *status = R_ST_TAG; + /* continues to next case */ + case R_ST_TAG: + if (c == '>') + *status = R_ST_NORMAL; + else if (c == '=') + *status = R_ST_EQL; + return 0; + case R_ST_EQL: + if (c == '"') + *status = R_ST_DQUOTE; + else if (c == '\'') + *status = R_ST_QUOTE; + else if (IS_SPACE(c)) + *status = R_ST_EQL; + else if (c == '>') + *status = R_ST_NORMAL; + else + *status = R_ST_TAG; + return 0; + case R_ST_QUOTE: + if (c == '\'') + *status = R_ST_TAG; + return 0; + case R_ST_DQUOTE: + if (c == '"') + *status = R_ST_TAG; + return 0; + case R_ST_AMP: + if (c == ';') { + *status = R_ST_NORMAL; + return 0; + } + else if (c != '#' && !IS_ALNUM(c) && c != '_') { + /* something's wrong! */ + *status = R_ST_NORMAL; + return 0; + } + else + return 0; + case R_ST_CMNT1: + switch (c) { + case '-': + *status = R_ST_CMNT2; + break; + case '>': + *status = R_ST_NORMAL; + break; + default: + *status = R_ST_IRRTAG; + } + return 0; + case R_ST_CMNT2: + switch (c) { + case '-': + *status = R_ST_CMNT; + break; + case '>': + *status = R_ST_NORMAL; + break; + default: + *status = R_ST_IRRTAG; + } + return 0; + case R_ST_CMNT: + if (c == '-') + *status = R_ST_NCMNT1; + return 0; + case R_ST_NCMNT1: + if (c == '-') + *status = R_ST_NCMNT2; + else + *status = R_ST_CMNT; + return 0; + case R_ST_NCMNT2: + switch (c) { + case '>': + *status = R_ST_NORMAL; + break; + case '-': + *status = R_ST_NCMNT2; + break; + default: + if (IS_SPACE(c)) + *status = R_ST_NCMNT3; + else + *status = R_ST_CMNT; + break; + } + break; + case R_ST_NCMNT3: + switch (c) { + case '>': + *status = R_ST_NORMAL; + break; + case '-': + *status = R_ST_NCMNT1; + break; + default: + if (IS_SPACE(c)) + *status = R_ST_NCMNT3; + else + *status = R_ST_CMNT; + break; + } + return 0; + case R_ST_IRRTAG: + if (c == '>') + *status = R_ST_NORMAL; + return 0; + } + /* notreached */ + return 0; +} + +int +read_token(Str buf, char **instr, int *status, int pre, int append) +{ + char *p; + int prev_status; + + if (!append) + Strclear(buf); + if (**instr == '\0') + return 0; + for (p = *instr; *p; p++) { + prev_status = *status; + next_status(*p, status); + switch (*status) { + case R_ST_NORMAL: + if (prev_status == R_ST_AMP && *p != ';') { + p--; + break; + } + if (prev_status == R_ST_NCMNT2 || prev_status == R_ST_NCMNT3 || + prev_status == R_ST_IRRTAG || prev_status == R_ST_CMNT1) { + if (prev_status == R_ST_CMNT1 && !append) + Strclear(buf); + p++; + goto proc_end; + } + Strcat_char(buf, (!pre && IS_SPACE(*p)) ? ' ' : *p); + if (ST_IS_REAL_TAG(prev_status)) { + *instr = p + 1; + if (buf->length < 2 || + buf->ptr[buf->length - 2] != '<' || + buf->ptr[buf->length - 1] != '>') + return 1; + Strshrink(buf, 2); + } + break; + case R_ST_TAG0: + case R_ST_TAG: + if (prev_status == R_ST_NORMAL && p != *instr) { + *instr = p; + *status = prev_status; + return 1; + } + if (*status == R_ST_TAG0 && + !REALLY_THE_BEGINNING_OF_A_TAG(p)) { + /* it seems that this '<' is not a beginning of a tag */ + Strcat_charp(buf, "<"); + *status = R_ST_NORMAL; + } + else + Strcat_char(buf, *p); + break; + case R_ST_EQL: + case R_ST_QUOTE: + case R_ST_DQUOTE: + case R_ST_AMP: + Strcat_char(buf, *p); + break; + case R_ST_CMNT: + case R_ST_IRRTAG: + if (!append) + Strclear(buf); + break; + case R_ST_CMNT1: + case R_ST_CMNT2: + case R_ST_NCMNT1: + case R_ST_NCMNT2: + case R_ST_NCMNT3: + /* do nothing */ + break; + } + } + proc_end: + *instr = p; + return 1; +} + +Str +correct_irrtag(int status) +{ + char c; + Str tmp = Strnew(); + + while (status != R_ST_NORMAL) { + switch (status) { + case R_ST_CMNT: /* required "-->" */ + case R_ST_NCMNT1: /* required "->" */ + c = '-'; + break; + case R_ST_NCMNT2: + case R_ST_NCMNT3: + case R_ST_IRRTAG: + case R_ST_CMNT1: + case R_ST_CMNT2: + case R_ST_TAG: + case R_ST_TAG0: + case R_ST_EQL: /* required ">" */ + c = '>'; + break; + case R_ST_QUOTE: + c = '\''; + break; + case R_ST_DQUOTE: + c = '"'; + break; + case R_ST_AMP: + c = ';'; + break; + default: + return tmp; + } + next_status(c, &status); + Strcat_char(tmp, c); + } + return tmp; +} + +/* authentication */ +struct auth_cookie * +find_auth(char *host, char *realm) +{ + struct auth_cookie *p; + + for (p = Auth_cookie; p != NULL; p = p->next) { + if (!Strcasecmp_charp(p->host, host) && + !Strcasecmp_charp(p->realm, realm)) + return p; + } + return NULL; +} + +Str +find_auth_cookie(char *host, char *realm) +{ + struct auth_cookie *p = find_auth(host, realm); + if (p) + return p->cookie; + return NULL; +} + +void +add_auth_cookie(char *host, char *realm, Str cookie) +{ + struct auth_cookie *p; + + p = find_auth(host, realm); + if (p) { + p->cookie = cookie; + return; + } + p = New(struct auth_cookie); + p->host = Strnew_charp(host); + p->realm = Strnew_charp(realm); + p->cookie = cookie; + p->next = Auth_cookie; + Auth_cookie = p; +} + +/* get last modified time */ +char * +last_modified(Buffer * buf) +{ + TextListItem *ti; + struct stat st; + + if (buf->document_header) { + for (ti = buf->document_header->first; ti; ti = ti->next) { + if (strncasecmp(ti->ptr, "Last-modified: ", 15) == 0) { + return ti->ptr + 15; + } + } + return "unknown"; + } + else if (buf->currentURL.scheme == SCM_LOCAL) { + if (stat(buf->currentURL.file, &st) < 0) + return "unknown"; + return ctime(&st.st_mtime); + } + return "unknown"; +} + +static char roman_num1[] = +{ + 'i', 'x', 'c', 'm', '*', +}; +static char roman_num5[] = +{ + 'v', 'l', 'd', '*', +}; + +static Str +romanNum2(int l, int n) +{ + Str s = Strnew(); + + switch (n) { + case 1: + case 2: + case 3: + for (; n > 0; n--) + Strcat_char(s, roman_num1[l]); + break; + case 4: + Strcat_char(s, roman_num1[l]); + Strcat_char(s, roman_num5[l]); + break; + case 5: + case 6: + case 7: + case 8: + Strcat_char(s, roman_num5[l]); + for (n -= 5; n > 0; n--) + Strcat_char(s, roman_num1[l]); + break; + case 9: + Strcat_char(s, roman_num1[l]); + Strcat_char(s, roman_num1[l + 1]); + break; + } + return s; +} + +Str +romanNumeral(int n) +{ + Str r = Strnew(); + + if (n <= 0) + return r; + if (n >= 4000) { + Strcat_charp(r, "**"); + return r; + } + Strcat(r, romanNum2(3, n / 1000)); + Strcat(r, romanNum2(2, (n % 1000) / 100)); + Strcat(r, romanNum2(1, (n % 100) / 10)); + Strcat(r, romanNum2(0, n % 10)); + + return r; +} + +Str +romanAlphabet(int n) +{ + Str r = Strnew(); + int l; + char buf[14]; + + if (n <= 0) + return r; + + l = 0; + while (n) { + buf[l++] = 'a' + (n - 1) % 26; + n = (n - 1) / 26; + } + l--; + for (; l >= 0; l--) + Strcat_char(r, buf[l]); + + return r; +} + +Str +quoteShell(char *string) +{ + Str str = Strnew(); + + while (*string != '\0') { + if (!IS_ALNUM(*string) && *string != '_' && *string != '.' && + *string != ':' && *string != '/') + Strcat_char(str, '\\'); + Strcat_char(str, *(string++)); + } + return str; +} + +void +mySystem(char *command, int background) +{ +#ifdef __EMX__ /* jsawa */ + if (background){ + Str cmd = Strnew_charp("start /f "); + Strcat_charp(cmd, command); + system(cmd->ptr); + }else + system(command); +#else + Str cmd = Strnew_charp(command); + if (background) + cmd = Sprintf("(%s) >/dev/null 2>&1 &", cmd->ptr); + system(cmd->ptr); +#endif +} + +char * +expandName(char *name) +{ + Str userName = NULL; + char *p; + struct passwd *passent, *getpwnam(const char *); + Str extpath = Strnew(); + + p = name; + if (*p == '/' && *(p + 1) == '~' && IS_ALPHA(*(p + 2))) { + if (personal_document_root != NULL) { + userName = Strnew(); + p += 2; + while (IS_ALNUM(*p) || *p == '_' || *p == '-') + Strcat_char(userName, *(p++)); + passent = getpwnam(userName->ptr); + if (passent == NULL) { + p = name; + goto rest; + } + Strcat_charp(extpath, passent->pw_dir); + Strcat_char(extpath, '/'); + Strcat_charp(extpath, personal_document_root); + if (Strcmp_charp(extpath, "/") == 0 && *p == '/') + p++; + } + } + else + p = expandPath(p); + rest: + Strcat_charp(extpath, p); + return extpath->ptr; +} + +static char *tmpf_base[MAX_TMPF_TYPE] = +{ + "tmp", "src", "frame", "cache" +}; +static unsigned int tmpf_seq[MAX_TMPF_TYPE]; + +Str +tmpfname(int type, char *ext) +{ + Str tmpf; + tmpf = Sprintf("%s/w3m%s%d-%d%s", + rc_dir, + tmpf_base[type], + (int) getpid(), + tmpf_seq[type]++, + (ext)? ext : "" + ); + return tmpf; +} + +#ifdef USE_COOKIE +static char *monthtbl[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static int +get_day(char **s) +{ + Str tmp = Strnew(); + int day; + char *ss = *s; + + if (!**s) + return -1; + + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + + day = atoi(tmp->ptr); + + if (day < 1 || day > 31) { + *s = ss; + return -1; + } + return day; +} + +static int +get_month(char **s) +{ + Str tmp = Strnew(); + int mon; + char *ss = *s; + + if (!**s) + return -1; + + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + if (tmp->length > 0) { + mon = atoi(tmp->ptr); + } + else { + while (**s && IS_ALPHA(**s)) + Strcat_char(tmp, *((*s)++)); + for (mon = 1; mon <= 12; mon++) { + if (strncmp(tmp->ptr, monthtbl[mon - 1], 3) == 0) + break; + } + } + if (mon < 1 || mon > 12) { + *s = ss; + return -1; + } + return mon; +} + +static int +get_year(char **s) +{ + Str tmp = Strnew(); + int year; + char *ss = *s; + + if (!**s) + return -1; + + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + if (tmp->length != 2 && tmp->length != 4) { + *s = ss; + return -1; + } + + year = atoi(tmp->ptr); + if (tmp->length == 2) { + if (year >= 70) + year += 1900; + else + year += 2000; + } + return year; +} + +static int +get_time(char **s, int *hour, int *min, int *sec) +{ + Str tmp = Strnew(); + char *ss = *s; + + if (!**s) + return -1; + + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + if (**s != ':') { + *s = ss; + return -1; + } + *hour = atoi(tmp->ptr); + + (*s)++; + Strclear(tmp); + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + if (**s != ':') { + *s = ss; + return -1; + } + *min = atoi(tmp->ptr); + + (*s)++; + Strclear(tmp); + while (**s && IS_DIGIT(**s)) + Strcat_char(tmp, *((*s)++)); + *sec = atoi(tmp->ptr); + + if (*hour < 0 || *hour >= 24 || + *min < 0 || *min >= 60 || + *sec < 0 || *sec >= 60) { + *s = ss; + return -1; + } + return 0; +} + +/* RFC 1123 or RFC 850 or ANSI C asctime() format string -> time_t */ +time_t +mymktime(char *timestr) +{ + char *s; + int day, mon, year, hour, min, sec; + + if (!(timestr && *timestr)) + return -1; + s = timestr; + +#ifdef DEBUG + fprintf(stderr, "mktime: %s\n", timestr); +#endif /* DEBUG */ + + while (*s && IS_ALPHA(*s)) + s++; + while (*s && !IS_ALNUM(*s)) + s++; + + if (IS_DIGIT(*s)) { + /* RFC 1123 or RFC 850 format */ + if ((day = get_day(&s)) == -1) + return -1; + + while (*s && !IS_ALNUM(*s)) + s++; + if ((mon = get_month(&s)) == -1) + return -1; + + while (*s && !IS_DIGIT(*s)) + s++; + if ((year = get_year(&s)) == -1) + return -1; + + while (*s && !IS_DIGIT(*s)) + s++; + if (!*s) { + hour = 0; + min = 0; + sec = 0; + } + else if (get_time(&s, &hour, &min, &sec) == -1) { + return -1; + } + } + else { + /* ANSI C asctime() format. */ + while (*s && !IS_ALNUM(*s)) + s++; + if ((mon = get_month(&s)) == -1) + return -1; + + while (*s && !IS_DIGIT(*s)) + s++; + if ((day = get_day(&s)) == -1) + return -1; + + while (*s && !IS_DIGIT(*s)) + s++; + if (get_time(&s, &hour, &min, &sec) == -1) + return -1; + + while (*s && !IS_DIGIT(*s)) + s++; + if ((year = get_year(&s)) == -1) + return -1; + } +#ifdef DEBUG + fprintf(stderr, "year=%d month=%d day=%d hour:min:sec=%d:%d:%d\n", + year, mon, day, hour, min, sec); +#endif /* DEBUG */ + + mon -= 3; + if (mon < 0) { + mon += 12; + year--; + } + day += (year - 1968) * 1461 / 4; + day += ((((mon * 153) + 2) / 5) - 672); + return (time_t) ((day * 60 * 60 * 24) + + (hour * 60 * 60) + + (min * 60) + + sec); +} + +#ifdef INET6 +#include <sys/socket.h> +#endif /* INET6 */ +#include <netdb.h> +char * +FQDN(char *host) +{ + char *p; +#ifndef INET6 + struct hostent *entry; +#else /* INET6 */ + int *af; +#endif /* INET6 */ + + if (host == NULL) + return NULL; + + if (strcasecmp(host, "localhost") == 0) + return host; + + for (p = host; *p && *p != '.'; p++); + + if (*p == '.') + return host; + +#ifndef INET6 + if (!(entry = gethostbyname(host))) + return NULL; + + return allocStr(entry->h_name, 0); +#else /* INET6 */ + for (af = ai_family_order_table[DNS_order];; af++) { + int error; + struct addrinfo hints; + struct addrinfo *res, *res0; + char *namebuf; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = *af; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, NULL, &hints, &res0); + if (error) { + if (*af == PF_UNSPEC) { + /* all done */ + break; + } + /* try next address family */ + continue; + } + for (res = res0; res != NULL; res = res->ai_next) { + if (res->ai_canonname) { + /* found */ + namebuf = strdup(res->ai_canonname); + freeaddrinfo(res0); + return namebuf; + } + } + freeaddrinfo(res0); + if (*af == PF_UNSPEC) { + break; + } + } + /* all failed */ + return NULL; +#endif /* INET6 */ +} + +#endif /* USE_COOKIE */ @@ -0,0 +1,6002 @@ +/* $Id: file.c,v 1.1 2001/11/08 05:14:50 a-ito Exp $ */ +#include "fm.h" +#include <sys/types.h> +#include "myctype.h" +#include <signal.h> +#include <setjmp.h> +#include <sys/wait.h> +#include <stdio.h> +#include <time.h> +#ifdef __EMX__ +#include <strings.h> +#endif /* __EMX__ */ +#include <sys/stat.h> +#include <fcntl.h> +/* foo */ + +#include "html.h" +#include "parsetagx.h" +#include "local.h" +#include "regex.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif /* not max */ +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif /* not min */ + +static void gunzip_stream(URLFile * uf); +static FILE *lessopen_stream(char *path); +static Buffer *loadcmdout(char *cmd, + Buffer * (*loadproc) (URLFile *, Buffer *), + Buffer *defaultbuf); +static void close_textarea(struct html_feed_environ *h_env); +static void addnewline(Buffer * buf, char *line, Lineprop * prop, +#ifdef ANSI_COLOR + Linecolor * color, +#endif + int pos, int nlines); + +static Lineprop propBuffer[LINELEN]; +#ifdef ANSI_COLOR +static Linecolor colorBuffer[LINELEN]; +#endif + +static JMP_BUF AbortLoading; + +static struct table *tables[MAX_TABLE]; +static struct table_mode table_mode[MAX_TABLE]; + +static Str cur_select; +static Str select_str; +static int select_is_multiple; +static int n_selectitem; +static Str cur_option; +static Str cur_option_value; +static Str cur_option_label; +static int cur_option_selected; +static int cur_status; +#ifdef MENU_SELECT +/* menu based <select> */ +FormSelectOption select_option[MAX_SELECT]; +static int n_select; +static int cur_option_maxwidth; +#endif /* MENU_SELECT */ + +static Str cur_textarea; +Str textarea_str[MAX_TEXTAREA]; +static int cur_textarea_size; +static int cur_textarea_rows; +static int n_textarea; + +static int http_response_code; + +#ifdef JP_CHARSET +static char content_charset = '\0'; +static char guess_charset(char *p); +#ifdef NEW_FORM +static char check_charset(char *p); +static char check_accept_charset(char *p); +#endif +char DocumentCode = '\0'; +#endif + +static Str save_line = NULL; +static int save_prevchar = ' '; + +struct link_stack { + int cmd; + short offset; + short pos; + struct link_stack *next; +}; + +static struct link_stack *link_stack = NULL; + +#define FORMSTACK_SIZE 10 +#define FRAMESTACK_SIZE 10 + +#ifdef USE_NNTP +#define Str_news_endline(s) ((s)->ptr[0]=='.'&&((s)->ptr[1]=='\n'||(s)->ptr[1]=='\r'||(s)->ptr[1]=='\0')) +#endif /* USE_NNTP */ + +#ifdef NEW_FORM +#define INITIAL_FORM_SIZE 10 +static FormList **forms; +static int *form_stack; +static int form_max = 0; +static int forms_size = 0; +#define cur_form_id ((form_sp >= 0)? form_stack[form_sp] : -1) +#else /* not NEW_FORM */ +static FormList *form_stack[FORMSTACK_SIZE]; +#endif /* not NEW_FORM */ +static int form_sp = 0; + +static int current_content_length; + +static int cur_hseq; + +#define MAX_UL_LEVEL 9 +#ifdef KANJI_SYMBOLS +char *ullevel[MAX_UL_LEVEL] = +{ + "��", "��", "��", "��", "��", "��", "��", "��", "��" +}; +#define HR_RULE "��" +#define HR_RULE_WIDTH 2 +#else /* not KANJI_SYMBOLS */ +char *ullevel[MAX_UL_LEVEL] = +{ + NBSP "*", NBSP "+", NBSP "o", NBSP "#", NBSP "@", NBSP "-", + NBSP "=", "**", "--" +}; +#define HR_RULE "\212" +#define HR_RULE_WIDTH 1 +#endif /* not KANJI_SYMBOLS */ + +#ifdef USE_COOKIE +/* This array should be somewhere else */ +char *violations[COO_EMAX] = { + "internal error", + "tail match failed", + "wrong number of dots", + "RFC 2109 4.3.2 rule 1", + "RFC 2109 4.3.2 rule 2.1", + "RFC 2109 4.3.2 rule 2.2", + "RFC 2109 4.3.2 rule 3", + "RFC 2109 4.3.2 rule 4", + "RFC XXXX 4.3.2 rule 5" +}; +#endif + +#define SAVE_BUF_SIZE 1536 + +#ifndef STRCHR +char * +strchr(char *s, char c) +{ + while (*s) { + if (*s == c) + return s; + s++; + } + return NULL; +} +#endif /* not STRCHR */ + +static MySignalHandler +KeyAbort(SIGNAL_ARG) +{ + LONGJMP(AbortLoading, 1); + SIGNAL_RETURN; +} + +int +currentLn(Buffer * buf) +{ + if (buf->currentLine) + return buf->currentLine->real_linenumber + 1; + else + return 1; +} + +static Buffer * +loadSomething(URLFile * f, + char *path, + Buffer * (*loadproc) (URLFile *, Buffer *), + Buffer * defaultbuf) +{ + Buffer *buf; + + if ((buf = loadproc(f, defaultbuf)) == NULL) + return NULL; + + buf->filename = path; + if (buf->buffername == NULL || buf->buffername[0] == '\0') + buf->buffername = lastFileName(path); + if (buf->currentURL.scheme == SCM_UNKNOWN) + buf->currentURL.scheme = f->scheme; + buf->real_scheme = f->scheme; + if (f->scheme == SCM_LOCAL && buf->sourcefile == NULL) + buf->sourcefile = path; + UFclose(f); + return buf; +} + +int +dir_exist(char *path) +{ + struct stat stbuf; + + if (path == NULL || *path == '\0') + return 0; + if (stat(path, &stbuf) == -1) + return 0; + return IS_DIRECTORY(stbuf.st_mode); +} + +static int +is_dump_text_type(char *type) +{ + struct mailcap *mcap; + return (type && (mcap = searchExtViewer(type)) && + (mcap->flags & (MAILCAP_HTMLOUTPUT|MAILCAP_COPIOUSOUTPUT))); +} + +static int +is_text_type(char *type) +{ + return (type == NULL || type[0] == '\0' || + strncasecmp(type, "text/", 5) == 0); +} + +static int +is_plain_text_type(char *type) +{ + return ((type && strcasecmp(type, "text/plain") == 0) || + (is_text_type(type) && !is_dump_text_type(type))); +} + +static void +check_compression(char *path, URLFile *uf) +{ + int len; + + if (path == NULL) + return; + + len = strlen(path); + uf->compression = CMP_NOCOMPRESS; + if (len > 2 && strcasecmp(&path[len - 2], ".Z") == 0) { + uf->compression = CMP_COMPRESS; + uf->guess_type = "application/x-compress"; + } + else if (len > 3 && strcasecmp(&path[len - 3], ".gz") == 0) { + uf->compression = CMP_GZIP; + uf->guess_type = "application/x-gzip"; + } + else if (len > 4 && strcasecmp(&path[len - 4], ".bz2") == 0) { + uf->compression = CMP_BZIP2; + uf->guess_type = "application/x-bzip"; + } +} + +static char * +compress_application_type(int compression) +{ + switch (compression) { + case CMP_COMPRESS: + return "application/x-compress"; + case CMP_GZIP: + return "application/x-gzip"; + case CMP_BZIP2: + return "application/x-bzip"; + case CMP_DEFLATE: + return "application/x-deflate"; + default: + return NULL; + } +} + +static char * +uncompressed_file_type(char *path, char **ext) +{ + int len, slen; + Str fn; + char *t0; + + if (path == NULL) + return NULL; + + len = strlen(path); + if (len > 2 && strcasecmp(&path[len - 2], ".Z") == 0) { + slen = 2; + } + else if (len > 3 && strcasecmp(&path[len - 3], ".gz") == 0) { + slen = 3; + } + else if (len > 4 && strcasecmp(&path[len - 4], ".bz2") == 0) { + slen = 4; + } + else + return NULL; + + fn = Strnew_charp(path); + Strshrink(fn, slen); + if (ext) + *ext = filename_extension(fn->ptr, 0); + t0 = guessContentType(fn->ptr); + if (t0 == NULL) + t0 = "text/plain"; + return t0; +} + +void +examineFile(char *path, URLFile *uf) +{ + struct stat stbuf; + + uf->guess_type = NULL; + if (path == NULL || *path == '\0' || + stat(path, &stbuf) == -1 || NOT_REGULAR(stbuf.st_mode)) { + uf->stream = NULL; + return; + } + uf->stream = openIS(path); + if (!do_download) { + if (use_lessopen && getenv("LESSOPEN") != NULL) { + FILE *fp; + uf->guess_type = guessContentType(path); + if (uf->guess_type == NULL) + uf->guess_type = "text/plain"; + if (strcasecmp(uf->guess_type, "text/html") == 0) + return; + if ((fp = lessopen_stream(path))) { + UFclose(uf); + uf->stream = newFileStream(fp, (void (*)()) pclose); + uf->guess_type = "text/plain"; + return; + } + } + check_compression(path, uf); + if (uf->compression != CMP_NOCOMPRESS) { + char *ext = uf->ext; + char *t0 = uncompressed_file_type(path, &ext); + uf->guess_type = t0; + uf->ext = ext; + gunzip_stream(uf); + return; + } + } +} + +/* + * convert line + */ +Str +convertLine(URLFile * uf, Str line, char *code, int mode) +{ +#ifdef JP_CHARSET + char ic; + if ((ic = checkShiftCode(line, *code)) != '\0') + line = conv_str(line, (*code = ic), InnerCode); +#endif /* JP_CHARSET */ + cleanup_line(line, mode); +#ifdef USE_NNTP + if (uf->scheme == SCM_NEWS) + Strchop(line); +#endif /* USE_NNTP */ + return line; +} + +/* + * loadFile: load file to buffer + */ +Buffer * +loadFile(char *path) +{ + URLFile uf; + init_stream(&uf, SCM_LOCAL, NULL); + examineFile(path, &uf); + if (uf.stream == NULL) + return NULL; + current_content_length = 0; + return loadSomething(&uf, path, loadBuffer, NULL); +} + +#ifdef USE_COOKIE +static int +matchattr(char *p, char *attr, int len, Str * value) +{ + int quoted; + char *q = NULL; + + if (strncasecmp(p, attr, len) == 0) { + p += len; + SKIP_BLANKS(p); + if (value) { + *value = Strnew(); + if (*p == '=') { + p++; + SKIP_BLANKS(p); + quoted = 0; + while (!IS_ENDL(*p) && (quoted || *p != ';')) { + if (!IS_SPACE(*p)) + q = p; + if (*p == '"') + quoted = (quoted) ? 0 : 1; + else + Strcat_char(*value, *p); + p++; + } + if (q) + Strshrink(*value, p - q - 1); + } + return 1; + } + else { + if (IS_ENDT(*p)) { + return 1; + } + } + } + return 0; +} +#endif /* USE_COOKIE */ + +void +readHeader(URLFile * uf, + Buffer * newBuf, + int thru, + ParsedURL * pu) +{ + char *p, *q, *emsg; + char c; + Str lineBuf2 = NULL; + Str tmp; + TextList *headerlist; +#ifdef JP_CHARSET + char code = DocumentCode, ic; +#endif + extern int w3m_dump_head; + + headerlist = newBuf->document_header = newTextList(); + if (uf->scheme == SCM_HTTP +#ifdef USE_SSL + || uf->scheme == SCM_HTTPS +#endif /* USE_SSL */ + ) + http_response_code = -1; + else + http_response_code = 0; + + while ((tmp = StrmyUFgets(uf))->length) { +#ifdef HTTP_DEBUG + { + FILE *ff; + ff = fopen("zzrequest", "a"); + Strfputs(tmp, ff); + fclose(ff); + } +#endif /* HTTP_DEBUG */ + cleanup_line(tmp, HEADER_MODE); + if ((tmp->ptr[0] == '\n' || + tmp->ptr[0] == '\r' || + tmp->ptr[0] == '\0') +#ifdef USE_NNTP + || + (uf->scheme == SCM_NEWS && + Str_news_endline(tmp) && + (iseos(uf->stream) = TRUE)) +#endif /* USE_NNTP */ + ) { + if (!lineBuf2) + /* there is no header */ + break; + /* last header */ + } + else if (!w3m_dump_head) { + if (lineBuf2) { + Strcat(lineBuf2, tmp); + } + else { + lineBuf2 = tmp; + } + c = UFgetc(uf); + UFundogetc(uf); + if (c == ' ' || c == '\t') + /* header line is continued */ + continue; + lineBuf2 = decodeMIME(lineBuf2->ptr); +#ifdef JP_CHARSET + if ((ic = checkShiftCode(lineBuf2, code)) != '\0') + lineBuf2 = conv_str(lineBuf2, (code = ic), InnerCode); +#endif /* JP_CHARSET */ + /* separated with line and stored */ + tmp = Strnew_size(lineBuf2->length); + for (p = lineBuf2->ptr; *p; p = q) { + for (q = p; *q && *q != '\r' && *q != '\n'; q++); + lineBuf2 = checkType(Strnew_charp(p), propBuffer, +#ifdef ANSI_COLOR + NULL, NULL, +#endif + min(LINELEN, q - p)); + Strcat(tmp, lineBuf2); + if (thru) + addnewline(newBuf, lineBuf2->ptr, propBuffer, +#ifdef ANSI_COLOR + NULL, +#endif + lineBuf2->length, -1); + for (; *q && (*q == '\r' || *q == '\n'); q++); + } + lineBuf2 = tmp; + } + else { + lineBuf2 = tmp; + } + if ((uf->scheme == SCM_HTTP +#ifdef USE_SSL + || uf->scheme == SCM_HTTPS +#endif /* USE_SSL */ + ) && http_response_code == -1) { + p = lineBuf2->ptr; + while (*p && !IS_SPACE(*p)) + p++; + while (*p && IS_SPACE(*p)) + p++; + http_response_code = atoi(p); + if (fmInitialized) { + message(lineBuf2->ptr, 0, 0); + refresh(); + } + } + if (!strncasecmp(lineBuf2->ptr, "content-transfer-encoding:", 26)) { + p = lineBuf2->ptr + 26; + while (IS_SPACE(*p)) + p++; + if (!strncasecmp(p, "base64", 6)) + uf->encoding = ENC_BASE64; + else if (!strncasecmp(p, "quoted-printable", 16)) + uf->encoding = ENC_QUOTE; + else + uf->encoding = ENC_7BIT; + } + else if (!strncasecmp(lineBuf2->ptr, "content-encoding:", 17)) { + p = lineBuf2->ptr + 17; + while (IS_SPACE(*p)) + p++; + if ((!strncasecmp(p, "x-gzip", 6) || !strncasecmp(p, "gzip", 4)) || + (!strncasecmp(p, "x-compress", 10) || !strncasecmp(p, "compress", 8))) { + uf->compression = CMP_GZIP; + } + else if (!strncasecmp(p, "x-bzip", 6) || + !strncasecmp(p, "bzip", 4) || + !strncasecmp(p, "bzip2", 5)) { + uf->compression = CMP_BZIP2; + } + else if (!strncasecmp(p, "x-deflate", 9) || + !strncasecmp(p, "deflate", 7)) { + uf->compression = CMP_DEFLATE; + } + } +#ifdef USE_COOKIE + else if (use_cookie && accept_cookie && + pu && check_cookie_accept_domain(pu->host) && + (!strncasecmp(lineBuf2->ptr, "Set-Cookie:", 11) || + !strncasecmp(lineBuf2->ptr, "Set-Cookie2:", 12))) { + Str name = Strnew(), value = Strnew(), domain = NULL, path = NULL, + comment = NULL, commentURL = NULL, port = NULL, tmp; + int version, quoted, flag = 0; + time_t expires = (time_t) - 1; + + q = NULL; + if (lineBuf2->ptr[10] == '2') { + p = lineBuf2->ptr + 12; + version = 1; + } + else { + p = lineBuf2->ptr + 11; + version = 0; + } +#ifdef DEBUG + fprintf(stderr, "Set-Cookie: [%s]\n", p); +#endif /* DEBUG */ + SKIP_BLANKS(p); + while (*p != '=' && !IS_ENDT(*p)) + Strcat_char(name, *(p++)); + Strremovetrailingspaces(name); + if (*p == '=') { + p++; + SKIP_BLANKS(p); + quoted = 0; + while (!IS_ENDL(*p) && (quoted || *p != ';')) { + if (!IS_SPACE(*p)) + q = p; + if (*p == '"') + quoted = (quoted) ? 0 : 1; + Strcat_char(value, *(p++)); + } + if (q) + Strshrink(value, p - q - 1); + } + while (*p == ';') { + p++; + SKIP_BLANKS(p); + if (matchattr(p, "expires", 7, &tmp)) { + /* version 0 */ + expires = mymktime(tmp->ptr); + } + else if (matchattr(p, "max-age", 7, &tmp)) { + /* XXX Is there any problem with max-age=0? (RFC 2109 ss. 4.2.1, 4.2.2 */ + expires = time(NULL) + atol(tmp->ptr); + } + else if (matchattr(p, "domain", 6, &tmp)) { + domain = tmp; + } + else if (matchattr(p, "path", 4, &tmp)) { + path = tmp; + } + else if (matchattr(p, "secure", 6, NULL)) { + flag |= COO_SECURE; + } + else if (matchattr(p, "comment", 7, &tmp)) { + comment = tmp; + } + else if (matchattr(p, "version", 7, &tmp)) { + version = atoi(tmp->ptr); + } + else if (matchattr(p, "port", 4, &tmp)) { + /* version 1, Set-Cookie2 */ + port = tmp; + } + else if (matchattr(p, "commentURL", 10, &tmp)) { + /* version 1, Set-Cookie2 */ + commentURL = tmp; + } + else if (matchattr(p, "discard", 7, NULL)) { + /* version 1, Set-Cookie2 */ + flag |= COO_DISCARD; + } + quoted = 0; + while (!IS_ENDL(*p) && (quoted || *p != ';')) { + if (*p == '"') + quoted = (quoted) ? 0 : 1; + p++; + } + } + if (pu && name->length > 0) { + int err; + if (flag & COO_SECURE) + disp_message_nsec("Received a secured cookie", FALSE, 1, TRUE, FALSE); + else + disp_message_nsec(Sprintf("Received cookie: %s=%s", + name->ptr, value->ptr)->ptr, FALSE, 1, TRUE, FALSE); + err = add_cookie(pu, name, value, expires, domain, path, flag, + comment, version, port, commentURL); + if (err) { + char *ans = (accept_bad_cookie == TRUE) ? "y" : NULL; + if (fmInitialized && (err & COO_OVERRIDE_OK) && + accept_bad_cookie == PERHAPS) { + Str msg = Sprintf("Accept bad cookie from %s for %s? (y or n) ", + pu->host, domain->ptr); + if (msg->length > COLS - 4) + Strshrink(msg, msg->length - (COLS - 4)); + term_raw(); + ans = inputStr(msg->ptr, FALSE); + } + if (ans == NULL || tolower(*ans) != 'y' || + (err = add_cookie(pu, name, value, expires, domain, path, + flag | COO_OVERRIDE, + comment, version, port, commentURL))) { + err = (err & ~COO_OVERRIDE_OK) - 1; + if (err >= 0 && err < COO_EMAX) + emsg = Sprintf("This cookie was rejected " + "to prevent security violation. [%s]", + violations[err])->ptr; + else + emsg = "This cookie was rejected to prevent security violation."; + record_err_message(emsg); + disp_message_nsec(emsg, FALSE, 10, TRUE, FALSE); + } else + disp_message_nsec(Sprintf("Accepting invalid cookie: %s=%s", + name->ptr,value->ptr)->ptr, FALSE, 1, TRUE, FALSE); + } + } + } +#endif /* USE_COOKIE */ + else if (!strncasecmp(lineBuf2->ptr, "w3m-control:", 12) && + uf->scheme == SCM_LOCAL_CGI) { + Str funcname = Strnew(); + int f; + extern int w3mNFuncList; + + p = lineBuf2->ptr + 12; + SKIP_BLANKS(p); + while (*p && !IS_SPACE(*p)) + Strcat_char(funcname, *(p++)); + SKIP_BLANKS(p); + f = getFuncList(funcname->ptr, w3mFuncList, w3mNFuncList); + if (f >= 0) { + tmp = Strnew_charp(p); + Strchop(tmp); + pushEvent(f, tmp->ptr); + } + } + if (headerlist) + pushText(headerlist, lineBuf2->ptr); + Strfree(lineBuf2); + lineBuf2 = NULL; + } + if (thru) + addnewline(newBuf, "", propBuffer, +#ifdef ANSI_COLOR + NULL, +#endif + 0, -1); +} + +char * +checkHeader(Buffer * buf, char *field) +{ + int len = strlen(field); + TextListItem *i; + char *p; + + if (buf == NULL || field == NULL) + return NULL; + for (i = buf->document_header->first; i != NULL; i = i->next) { + if (!strncasecmp(i->ptr, field, len)) { + p = i->ptr + len; + SKIP_BLANKS(p); + return p; + } + } + return NULL; +} + +char * +checkContentType(Buffer * buf) +{ + char *p; + Str r; + p = checkHeader(buf, "Content-Type:"); + if (p == NULL) + return NULL; + r = Strnew(); + while (*p && *p != ';' && !IS_SPACE(*p)) + Strcat_char(r, *p++); +#ifdef JP_CHARSET + content_charset = '\0'; + if ((p = strcasestr(p, "charset")) != NULL) { + p += 7; + SKIP_BLANKS(p); + if (*p == '=') { + p++; + SKIP_BLANKS(p); + content_charset = guess_charset(p); + } + } +#endif + return r->ptr; +} + +static Str +extractRealm(char *q) +{ + Str p = Strnew(); + char c; + + SKIP_BLANKS(q); + if (strncasecmp(q, "Basic ", 6) != 0) { + /* complicated authentication... not implemented */ + return NULL; + } + q += 6; + SKIP_BLANKS(q); + if (strncasecmp(q, "realm=", 6) != 0) { + /* no realm attribute... get confused */ + return NULL; + } + q += 6; + SKIP_BLANKS(q); + c = '\0'; + if (*q == '"') + c = *q++; + while (*q != '\0' && *q != c) + Strcat_char(p, *q++); + return p; +} + +static Str +getAuthCookie(char *realm, char *auth_header, TextList * extra_header, ParsedURL * pu) +{ + Str ss; + Str uname, pwd; + Str tmp; + TextListItem *i, **i0; + int a_found; + int auth_header_len = strlen(auth_header); + + a_found = FALSE; + for (i0 = &(extra_header->first), i = *i0; i != NULL; i0 = &(i->next), i = *i0) { + if (!strncasecmp(i->ptr, auth_header, auth_header_len)) { + a_found = TRUE; + break; + } + } + if (a_found) { + /* This means that *-Authenticate: header is received after * + * Authorization: header is sent to the server. */ + if (fmInitialized) { + message("Wrong username or password", 0, 0); + refresh(); + } + else + fprintf(stderr, "Wrong username or password\n"); + sleep(1); + ss = NULL; + *i0 = i->next; /* delete Authenticate: header from * + * extra_header */ + } + else + ss = find_auth_cookie(pu->host, realm); + if (ss == NULL) { + /* input username and password */ + sleep(2); + if (fmInitialized) { + char *pp; + term_raw(); + if ((pp = inputStr(Sprintf("Username for %s: ", realm)->ptr, NULL)) == NULL) + return NULL; + uname = Strnew_charp(pp); + if ((pp = inputLine(Sprintf("Password for %s: ", realm)->ptr, NULL, IN_PASSWORD)) == NULL) + return NULL; + pwd = Strnew_charp(pp); + term_cbreak(); + } + else { + printf("Username: "); + fflush(stdout); + uname = Strfgets(stdin); + Strchop(uname); + pwd = Strnew_charp((char *) getpass("Password: ")); + } + Strcat_char(uname, ':'); + Strcat(uname, pwd); + ss = encodeB(uname->ptr); + } + tmp = Strnew_charp(auth_header); + Strcat_charp(tmp, " Basic "); + Strcat(tmp, ss); + Strcat_charp(tmp, "\r\n"); + pushText(extra_header, tmp->ptr); + return ss; +} + +/* + * loadGeneralFile: load file to buffer + */ +Buffer * +loadGeneralFile(char *path, ParsedURL * current, char *referer, int flag, FormList * request) +{ + URLFile f, *of = NULL; + ParsedURL pu; + Buffer *b = NULL, *(*proc) (); + char *tpath; + char *t, *p, *real_type = NULL; + Buffer *t_buf = NULL; + int searchHeader = SearchHeader; + int searchHeader_through = TRUE; + MySignalHandler(*prevtrap) (); + TextList *extra_header = newTextList(); + Str ss; + Str realm; + int add_auth_cookie_flag; + unsigned char status = HTST_NORMAL; + URLOption url_option; + Str tmp; + extern int w3m_dump, w3m_dump_source; + + tpath = path; + prevtrap = NULL; + add_auth_cookie_flag = 0; + + if (proxy_auth_cookie != NULL) { + pushText(extra_header, Sprintf("Proxy-Authorization: Basic %s\r\n", + proxy_auth_cookie->ptr)->ptr); + } + load_doc: + url_option.referer = referer; + url_option.flag = flag; + f = openURL(tpath, &pu, current, &url_option, request, extra_header, of, &status); + of = NULL; +#ifdef JP_CHARSET + content_charset = '\0'; +#endif + if (f.stream == NULL) { + /* openURL failure: it means either (1) the requested URL is a directory name + * on an FTP server, or (2) is a local directory name. + */ + extern Str FTPDIRtmp; + if (fmInitialized && prevtrap) { + term_raw(); + signal(SIGINT, prevtrap); + } + switch (f.scheme) { + case SCM_FTPDIR: + if (FTPDIRtmp->length > 0) { + b = loadHTMLString(FTPDIRtmp); + if (b) { + if (b->currentURL.host == NULL && b->currentURL.file == NULL) + copyParsedURL(&b->currentURL, &pu); + b->real_scheme = pu.scheme; + } + return b; + } + break; + case SCM_LOCAL: + { + struct stat st; + if (stat(pu.file, &st) < 0) + return NULL; + if (S_ISDIR(st.st_mode)) { + if (UseExternalDirBuffer) { + Str tmp = Strnew_charp(DirBufferCommand); + Strcat_m_charp(tmp, "?", + pu.file, + "#current", + NULL); + b = loadGeneralFile(tmp->ptr, NULL, NO_REFERER, 0, NULL); + if (b != NULL && b != NO_BUFFER) { + copyParsedURL(&b->currentURL, &pu); + b->filename = b->currentURL.file; + } + return b; + } + else { + b = dirBuffer(pu.file); + if (b == NULL) + return NULL; + t = "text/html"; + b->real_scheme = pu.scheme; + goto loaded; + } + } + } + } + return NULL; + } + + /* openURL() succeeded */ + if (SETJMP(AbortLoading) != 0) { + /* transfer interrupted */ + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + if (b) + discardBuffer(b); + UFclose(&f); + return NULL; + } + + b = NULL; + if (f.is_cgi) { + /* local CGI */ + searchHeader = TRUE; + searchHeader_through = FALSE; + } + if (fmInitialized) { + prevtrap = signal(SIGINT, KeyAbort); + term_cbreak(); + } + if (pu.scheme == SCM_HTTP || +#ifdef USE_SSL + pu.scheme == SCM_HTTPS || +#endif /* USE_SSL */ + (( +#ifdef USE_GOPHER + (pu.scheme == SCM_GOPHER && non_null(GOPHER_proxy)) || +#endif /* USE_GOPHER */ + (pu.scheme == SCM_FTP && non_null(FTP_proxy)) + ) && !Do_not_use_proxy && !check_no_proxy(pu.host))) { + + if (fmInitialized) { + message(Sprintf("%s contacted. Waiting for reply...", pu.host)->ptr, 0, 0); + refresh(); + } + if (t_buf == NULL) + t_buf = newBuffer(INIT_BUFFER_WIDTH); +#ifdef USE_SSL + if (IStype(f.stream) == IST_SSL) { + Str s = ssl_get_certificate(f.stream); + if (s != NULL) + t_buf->ssl_certificate = s->ptr; + } +#endif + readHeader(&f, t_buf, FALSE, &pu); + t = checkContentType(t_buf); + if (t == NULL) + t = "text/plain"; + if (http_response_code >= 301 && http_response_code <= 303 && (p = checkHeader(t_buf, "Location:")) != NULL) { + /* document moved */ + tmp = Strnew_charp(p); + Strchop(tmp); + tpath = tmp->ptr; + request = NULL; + UFclose(&f); + add_auth_cookie_flag = 0; + current = New(ParsedURL); + copyParsedURL(current, &pu); + t_buf->bufferprop |= BP_REDIRECTED; + status = HTST_NORMAL; + goto load_doc; + } + if ((p = checkHeader(t_buf, "WWW-Authenticate:")) != NULL && + http_response_code == 401) { + /* Authentication needed */ + realm = extractRealm(p); + if (realm != NULL) { + ss = getAuthCookie(realm->ptr, "Authorization:", extra_header, &pu); + if (ss == NULL) { + /* abort */ + UFclose(&f); + signal(SIGINT, prevtrap); + return NULL; + } + UFclose(&f); + add_auth_cookie_flag = 1; + status = HTST_NORMAL; + goto load_doc; + } + } + if ((p = checkHeader(t_buf, "Proxy-Authenticate:")) != NULL && + http_response_code == 407) { + /* Authentication needed */ + realm = extractRealm(p); + if (realm != NULL) { + ss = getAuthCookie(realm->ptr, + "Proxy-Authorization:", + extra_header, + &HTTP_proxy_parsed); + proxy_auth_cookie = ss; + if (ss == NULL) { + /* abort */ + UFclose(&f); + signal(SIGINT, prevtrap); + return NULL; + } + UFclose(&f); + status = HTST_NORMAL; + goto load_doc; + } + } + if (add_auth_cookie_flag) + /* If authorization is required and passed */ + add_auth_cookie(pu.host, realm->ptr, ss); + if (status == HTST_CONNECT) { + of = &f; + goto load_doc; + } + } +#ifdef USE_NNTP + else if (pu.scheme == SCM_NEWS) { + t_buf = newBuffer(INIT_BUFFER_WIDTH); + readHeader(&f, t_buf, TRUE, &pu); + t = checkContentType(t_buf); + if (t == NULL) + t = "text/plain"; + } +#endif /* USE_NNTP */ +#ifdef USE_GOPHER + else if (pu.scheme == SCM_GOPHER) { + switch (*pu.file) { + case '0': + t = "text/plain"; + break; + case '1': + t = "gopher:directory"; + break; + case 'm': + t = "gopher:directory"; + break; + case 's': + t = "audio/basic"; + break; + case 'g': + t = "image/gif"; + break; + case 'h': + t = "text/html"; + break; + } + } +#endif /* USE_GOPHER */ + else if (pu.scheme == SCM_FTP) { + check_compression(path, &f); + if (f.compression != CMP_NOCOMPRESS) { + char *t1 = uncompressed_file_type(pu.file, NULL); + real_type = f.guess_type; + if (t1 && strncasecmp(t1, "application/", 12) == 0) { + f.compression = CMP_NOCOMPRESS; + t = real_type; + } + else if (t1) + t = t1; + else + t = real_type; + } + else { + real_type = guessContentType(pu.file); + if (real_type == NULL) + real_type = "text/plain"; + t = real_type; + } + if (!strncasecmp(t, "application/", 12)) { + char *tmpf = tmpfname(TMPF_DFL, NULL)->ptr; + current_content_length = 0; + if (save2tmp(f, tmpf) < 0) + UFclose(&f); + else { + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + doFileMove(tmpf, guess_save_name(pu.file)); + } + return NO_BUFFER; + } + } + else if (searchHeader) { + t_buf = newBuffer(INIT_BUFFER_WIDTH); + readHeader(&f, t_buf, searchHeader_through, &pu); + t = checkContentType(t_buf); + if (t == NULL) + t = "text/plain"; + if (f.is_cgi && (p = checkHeader(t_buf, "Location:")) != NULL) { + /* document moved */ + tmp = Strnew_charp(p); + Strchop(tmp); + tpath = tmp->ptr; + request = NULL; + UFclose(&f); + add_auth_cookie_flag = 0; + current = New(ParsedURL); + copyParsedURL(current, &pu); + t_buf->bufferprop |= BP_REDIRECTED; + status = HTST_NORMAL; + goto load_doc; + } + searchHeader = SearchHeader = FALSE; + } + else if (DefaultType) { + t = DefaultType; + DefaultType = NULL; + } + else { + t = guessContentType(pu.file); + if (t == NULL) + t = "text/plain"; + real_type = t; + if (f.guess_type) + t = f.guess_type; + } + if (real_type == NULL) + real_type = t; + proc = loadBuffer; + + if (do_download) { + /* download only */ + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + doFileSave(f, guess_save_name(pu.file)); + UFclose(&f); + return NO_BUFFER; + } + + if (f.compression != CMP_NOCOMPRESS) { + if (!w3m_dump_source && + (w3m_dump || is_text_type(t) || searchExtViewer(t))) { + gunzip_stream(&f); + uncompressed_file_type(pu.file, &f.ext); + } + else { + t = compress_application_type(f.compression); + f.compression = CMP_NOCOMPRESS; + } + } + + if (!strcasecmp(t, "text/html")) + proc = loadHTMLBuffer; + else if (is_plain_text_type(t)) + proc = loadBuffer; +#ifdef USE_GOPHER + else if (!strcasecmp(t, "gopher:directory")) { + proc = loadGopherDir; + } +#endif /* USE_GOPHER */ + else if (w3m_backend) ; + else if (!w3m_dump || is_dump_text_type(t)) { + if (!do_download && doExternal(f, pu.file, t, &b, t_buf)) { + if (b && b != NO_BUFFER) { + b->real_scheme = f.scheme; + b->real_type = real_type; + if (b->currentURL.host == NULL && b->currentURL.file == NULL) + copyParsedURL(&b->currentURL, &pu); + } + UFclose(&f); + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + return b; + } + else { + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + if (pu.scheme == SCM_LOCAL) { + UFclose(&f); + doFileCopy(pu.file, guess_save_name(pu.file)); + } + else { + doFileSave(f, guess_save_name(pu.file)); + UFclose(&f); + } + return NO_BUFFER; + } + } + + current_content_length = 0; + if ((p = checkHeader(t_buf, "Content-Length:")) != NULL) + current_content_length = atoi(p); + + if (flag & RG_FRAME) { + t_buf = newBuffer(INIT_BUFFER_WIDTH); + t_buf->bufferprop |= BP_FRAME; + } +#ifdef USE_SSL + if (IStype(f.stream) == IST_SSL) { + Str s = ssl_get_certificate(f.stream); + if (s != NULL) + t_buf->ssl_certificate = s->ptr; + } +#endif + b = loadSomething(&f, pu.file, proc, t_buf); + UFclose(&f); + if (b) { + b->real_scheme = f.scheme; + b->real_type = real_type; + loaded: + if (b->currentURL.host == NULL && b->currentURL.file == NULL) + copyParsedURL(&b->currentURL, &pu); + if (!strcmp(t, "text/html")) + b->type = "text/html"; + else if (w3m_backend) { + Str s = Strnew_charp(t); + b->type = s->ptr; + } + else + b->type = "text/plain"; + if (pu.label) { + if (proc == loadHTMLBuffer) { + Anchor *a; +#ifdef JP_CHARSET + a = searchURLLabel(b, conv(pu.label, b->document_code, InnerCode)->ptr); +#else /* not JP_CHARSET */ + a = searchURLLabel(b, pu.label); +#endif /* not JP_CHARSET */ + if (a != NULL) { + gotoLine(b, a->start.line); + b->pos = a->start.pos; + arrangeCursor(b); + } + } + else { /* plain text */ + int l = atoi(pu.label); + gotoLine(b,l); + b->pos = 0; + arrangeCursor(b); + } + } + } + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + return b; +} + +#define TAG_IS(s,tag,len)\ + (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len]))) + +static char * +has_hidden_link(struct readbuffer *obuf, int cmd) +{ + Str line = obuf->line; + struct link_stack *p; + + if (Strlastchar(line) != '>') + return NULL; + + for (p = link_stack; p; p = p->next) + if (p->cmd == cmd) + break; + if (!p) + return NULL; + + if (obuf->pos == p->pos) + return line->ptr + p->offset; + + return NULL; +} + +static void +push_link(int cmd, int offset, int pos) +{ + struct link_stack *p; + p = New(struct link_stack); + p->cmd = cmd; + p->offset = offset; + p->pos = pos; + p->next = link_stack; + link_stack = p; +} + +static int +is_period_char(int ch) +{ + switch (ch) { + case ',': + case '.': + case ':': + case ';': + case '?': + case '!': + case ')': + case ']': + case '}': + case '>': + return 1; + default: + return 0; + } +} + +static int +is_beginning_char(int ch) +{ + switch (ch) { + case '(': + case '[': + case '{': + case '`': + case '<': + return 1; + default: + return 0; + } +} + +static int +is_word_char(int ch) +{ +#ifdef JP_CHARSET + if (is_wckanji(ch) || IS_CNTRL(ch)) + return 0; +#else + if (IS_CNTRL(ch)) + return 0; +#endif + + if (IS_ALNUM(ch)) + return 1; + + switch (ch) { + case ',': + case '.': + case '\"': /* " */ + case '\'': + case '$': + case '%': + case '+': + case '-': + case '@': + case '~': + case '_': + return 1; + } + if (ch == TIMES_CODE || ch == DIVIDE_CODE || ch == ANSP_CODE) + return 0; + if (ch >= AGRAVE_CODE || ch == NBSP_CODE) + return 1; + return 0; +} + +int +is_boundary(int ch1, int ch2) +{ + if (!ch1 || !ch2) + return 1; + + if (ch1 == ' ' && ch2 == ' ') + return 0; + + if (ch1 != ' ' && is_period_char(ch2)) + return 0; + + if (ch2 != ' ' && is_beginning_char(ch1)) + return 0; + + if (is_word_char(ch1) && is_word_char(ch2)) + return 0; + + return 1; +} + + +static void +set_breakpoint(struct readbuffer *obuf, int tag_length) +{ + obuf->bp.len = obuf->line->length; + obuf->bp.pos = obuf->pos; + obuf->bp.tlen = tag_length; + obuf->bp.flag = obuf->flag; +#ifdef FORMAT_NICE + obuf->bp.flag &= ~RB_FILL; +#endif /* FORMAT_NICE */ + + if (!obuf->bp.init_flag) + return; + + obuf->bp.anchor = obuf->anchor; + obuf->bp.anchor_target = obuf->anchor_target; + obuf->bp.anchor_hseq = obuf->anchor_hseq; + obuf->bp.img_alt = obuf->img_alt; + obuf->bp.in_bold = obuf->in_bold; + obuf->bp.in_under = obuf->in_under; + obuf->bp.nobr_level = obuf->nobr_level; + obuf->bp.prev_ctype = obuf->prev_ctype; + obuf->bp.init_flag = 0; +} + +static void +back_to_breakpoint(struct readbuffer *obuf) +{ + obuf->flag = obuf->bp.flag; + obuf->anchor = obuf->bp.anchor; + obuf->anchor_target = obuf->bp.anchor_target; + obuf->anchor_hseq = obuf->bp.anchor_hseq; + obuf->img_alt = obuf->bp.img_alt; + obuf->in_bold = obuf->bp.in_bold; + obuf->in_under = obuf->bp.in_under; + obuf->prev_ctype = obuf->bp.prev_ctype; + obuf->pos = obuf->bp.pos; + if (obuf->flag & RB_NOBR) + obuf->nobr_level = obuf->bp.nobr_level; +} + +static void +append_tags(struct readbuffer *obuf) +{ + int i; + int len = obuf->line->length; + int set_bp = 0; + + for (i = 0; i < obuf->tag_sp; i++) { + switch (obuf->tag_stack[i]->cmd) { + case HTML_A: + case HTML_IMG_ALT: + case HTML_B: + case HTML_U: + push_link(obuf->tag_stack[i]->cmd, obuf->line->length, obuf->pos); + break; + } + Strcat_charp(obuf->line, obuf->tag_stack[i]->cmdname); + switch (obuf->tag_stack[i]->cmd) { + case HTML_NOBR: + if (obuf->nobr_level > 1) + break; + case HTML_WBR: + set_bp = 1; + break; + } + } + obuf->tag_sp = 0; + if (set_bp) + set_breakpoint(obuf, obuf->line->length - len); +} + +static void +push_tag(struct readbuffer *obuf, char *cmdname, int cmd) +{ + obuf->tag_stack[obuf->tag_sp] = New(struct cmdtable); + obuf->tag_stack[obuf->tag_sp]->cmdname = allocStr(cmdname, 0); + obuf->tag_stack[obuf->tag_sp]->cmd = cmd; + obuf->tag_sp++; + if (obuf->tag_sp >= TAG_STACK_SIZE || + obuf->flag & (RB_SPECIAL & ~RB_NOBR)) + append_tags(obuf); +} + +static void +push_nchars(struct readbuffer *obuf, int width, + char *str, int len, Lineprop mode) +{ + int delta = get_mclen(mode); + append_tags(obuf); + Strcat_charp_n(obuf->line, str, len); + obuf->pos += width; + if (width > 0 && len >= delta) { + obuf->prevchar = mctowc(&str[len - delta], mode); + obuf->prev_ctype = mode; + } + obuf->flag |= RB_NFLUSHED; +} + +#define push_charp(obuf, width, str, mode)\ +push_nchars(obuf, width, str, strlen(str), mode) + +#define push_str(obuf, width, str, mode)\ +push_nchars(obuf, width, str->ptr, str->length, mode) + +static void +check_breakpoint(struct readbuffer *obuf, int pre_mode, int ch) +{ + int tlen, len = obuf->line->length; + + append_tags(obuf); + if (pre_mode) + return; + tlen = obuf->line->length - len; + if (tlen > 0 || is_boundary(obuf->prevchar, ch)) + set_breakpoint(obuf, tlen); +} + +static void +push_char(struct readbuffer *obuf, int pre_mode, char ch) +{ + check_breakpoint(obuf, pre_mode, (unsigned char)ch); + Strcat_char(obuf->line, ch); + obuf->pos++; + obuf->prevchar = (unsigned char) ch; + if (ch != ' ') + obuf->prev_ctype = PC_ASCII; + obuf->flag |= RB_NFLUSHED; +} + +#define PUSH(c) push_char(obuf, obuf->flag & RB_SPECIAL, c) + +static void +push_spaces(struct readbuffer *obuf, int pre_mode, int width) +{ + int i; + + if (width <= 0) + return; + check_breakpoint(obuf, pre_mode, ' '); + for (i = 0; i < width; i++) + Strcat_char(obuf->line, ' '); + obuf->pos += width; + obuf->prevchar = ' '; + obuf->flag |= RB_NFLUSHED; +} + +static void +proc_mchar(struct readbuffer *obuf, int pre_mode, + int width, char **str, Lineprop mode) +{ + int ch = mctowc(*str, mode); + + check_breakpoint(obuf, pre_mode, ch); + obuf->pos += width; +#ifdef JP_CHARSET + if (IS_KANJI1(**str) && mode == PC_ASCII) + Strcat_char(obuf->line, ' '); + else if (mode == PC_KANJI) + Strcat_charp_n(obuf->line, *str, 2); + else +#endif + Strcat_char(obuf->line, **str); + if (width > 0) { + obuf->prevchar = ch; + if (ch != ' ') + obuf->prev_ctype = mode; + } + (*str) += get_mclen(mode); + obuf->flag |= RB_NFLUSHED; +} + +void +push_render_image(Str str, int width, struct html_feed_environ *h_env) +{ + struct readbuffer *obuf = h_env->obuf; + int indent = h_env->envs[h_env->envc].indent; + + push_str(obuf, width, str, PC_ASCII); + if (width > 0) + flushline(h_env, obuf, indent, 0, h_env->limit); +} + +static int +sloppy_parse_line(char **str) +{ + if (**str == '<') { + while (**str && **str != '>') + (*str)++; + if (**str == '>') + (*str)++; + return 1; + } + else { + while (**str && **str != '<') + (*str)++; + return 0; + } +} + +static void +passthrough(struct readbuffer *obuf, char *str, int back) +{ + int status, cmd; + Str tok = Strnew(); + char *str_bak; + + if (back) { + Str str_save = Strnew_charp(str); + Strshrink(obuf->line, obuf->line->ptr + obuf->line->length - str); + str = str_save->ptr; + } + while (*str) { + str_bak = str; + if (sloppy_parse_line(&str)) { + char *q = str_bak; + cmd = gethtmlcmd(&q, &status); + if (back) { + struct link_stack *p; + for (p = link_stack; p; p = p->next) { + if (p->cmd == cmd) { + link_stack = p->next; + break; + } + } + back = 0; + } + else { + Strcat_charp_n(tok, str_bak, str - str_bak); + push_tag(obuf, tok->ptr, cmd); + Strclear(tok); + } + } + else { + push_nchars(obuf, 0, str_bak, str - str_bak, obuf->prev_ctype); + } + } +} + +#if 0 +int +is_blank_line(char *line, int indent) +{ + int i, is_blank = 0; + + for (i = 0; i < indent; i++) { + if (line[i] == '\0') { + is_blank = 1; + } + else if (line[i] != ' ') { + break; + } + } + if (i == indent && line[i] == '\0') + is_blank = 1; + return is_blank; +} +#endif + +void +fillline(struct readbuffer *obuf, int indent) +{ + push_spaces(obuf, 1, indent - obuf->pos); + obuf->flag &= ~RB_NFLUSHED; +} + +void +flushline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent, int force, int width) +{ + TextLineList *buf = h_env->buf; + FILE *f = h_env->f; + Str line = obuf->line, pass = NULL; + char *hidden_anchor = NULL, *hidden_img = NULL, *hidden_bold = NULL, + *hidden_under = NULL, *hidden = NULL; + + if (w3m_debug) { + FILE *f = fopen("zzzproc1", "a"); + fprintf(f, "flushline(%s,%d,%d,%d)\n", obuf->line->ptr, indent, force, width); + if (buf) { + TextLineListItem *p; + for (p = buf->first; p; p = p->next) { + fprintf(f, "buf=\"%s\"\n", p->ptr->line->ptr); + } + } + fclose(f); + } + + if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && Strlastchar(line) == ' ') { + Strshrink(line, 1); + obuf->pos--; + } + + append_tags(obuf); + + if (obuf->anchor) + hidden = hidden_anchor = has_hidden_link(obuf, HTML_A); + if (obuf->img_alt) { + if ((hidden_img = has_hidden_link(obuf, HTML_IMG_ALT)) != NULL) { + if (!hidden || hidden_img < hidden) + hidden = hidden_img; + } + } + if (obuf->in_bold) { + if ((hidden_bold = has_hidden_link(obuf, HTML_B)) != NULL) { + if (!hidden || hidden_bold < hidden) + hidden = hidden_bold; + } + } + if (obuf->in_under) { + if ((hidden_under = has_hidden_link(obuf, HTML_U)) != NULL) { + if (!hidden || hidden_under < hidden) + hidden = hidden_under; + } + } + if (hidden) { + pass = Strnew_charp(hidden); + Strshrink(line, line->ptr + line->length - hidden); + } + + if (!(obuf->flag & (RB_SPECIAL & ~RB_NOBR)) && obuf->pos > width) { + char *tp = &line->ptr[obuf->bp.len - obuf->bp.tlen]; + char *ep = &line->ptr[line->length]; + + if (obuf->bp.pos == obuf->pos && tp <= ep && + tp > line->ptr && tp[-1] == ' ') { + bcopy(tp, tp - 1, ep - tp + 1); + line->length--; + obuf->pos--; + } + } + + if (obuf->anchor && !hidden_anchor) + Strcat_charp(line, "</a>"); + if (obuf->img_alt && !hidden_img) + Strcat_charp(line, "</img_alt>"); + if (obuf->in_bold && !hidden_bold) + Strcat_charp(line, "</b>"); + if (obuf->in_under && !hidden_under) + Strcat_charp(line, "</u>"); + + if (force == 1 || obuf->flag & RB_NFLUSHED) { + TextLine *lbuf = newTextLine(line, obuf->pos); + if (RB_GET_ALIGN(obuf) == RB_CENTER) { + align(lbuf, width, ALIGN_CENTER); + } + else if (RB_GET_ALIGN(obuf) == RB_RIGHT) { + align(lbuf, width, ALIGN_RIGHT); + } +#ifdef FORMAT_NICE + else if (obuf->flag & RB_FILL) { + char *p; + int rest, rrest; + int nspace, d, i; + + rest = width - line->length; + if (rest > 1) { + nspace = 0; + for (p = line->ptr + indent; *p; p++) { + if (*p == ' ') + nspace++; + } + if (nspace > 0) { + int indent_here = 0; + d = rest / nspace; + p = line->ptr; + while (IS_SPACE(*p)) { + p++; + indent_here++; + } + rrest = rest - d * nspace; + line = Strnew_size(width + 1); + for (i = 0; i < indent_here; i++) + Strcat_char(line, ' '); + for (; *p; p++) { + Strcat_char(line, *p); + if (*p == ' ') { + for (i = 0; i < d; i++) + Strcat_char(line, ' '); + if (rrest > 0) { + Strcat_char(line, ' '); + rrest--; + } + } + } + lbuf = newTextLine(line, width); + } + } + } +#endif /* FORMAT_NICE */ +#ifdef TABLE_DEBUG + if (w3m_debug) { + FILE *f = fopen("zzzproc1", "a"); + fprintf(f, "pos=%d,%d, maxlimit=%d\n", + visible_length(lbuf->line->ptr), lbuf->pos, h_env->maxlimit); + fclose(f); + } +#endif + if (lbuf->pos > h_env->maxlimit) + h_env->maxlimit = lbuf->pos; + if (buf) { + pushTextLine(buf, lbuf); + if (w3m_backend) { + Strcat(backend_halfdump_str, lbuf->line); + Strcat_char(backend_halfdump_str, '\n'); + } + } + else { + Strfputs(lbuf->line, f); + fputc('\n', f); + } + if (obuf->flag & RB_SPECIAL || obuf->flag & RB_NFLUSHED) + h_env->blank_lines = 0; + else + h_env->blank_lines++; + } + else { + char *p = line->ptr, *q; + Str tmp = Strnew(), tmp2 = Strnew(); + +#define APPEND(str) \ + if (buf) { \ + appendTextLine(buf,(str),0); \ + if (w3m_backend) \ + Strcat(backend_halfdump_str, (str)); \ + } else \ + Strfputs((str),f) + + while (*p) { + q = p; + if (sloppy_parse_line(&p)) { + Strcat_charp_n(tmp, q, p - q); + if (force == 2) + APPEND(tmp); + else + Strcat(tmp2, tmp); + Strclear(tmp); + } + } + if (force == 2) { + if (pass) { + APPEND(pass); + } + pass = NULL; + } + else { + if (pass) + Strcat(tmp2, pass); + pass = tmp2; + } + } + obuf->line = Strnew_size(256); + obuf->pos = 0; + obuf->prevchar = ' '; + obuf->bp.init_flag = 1; + obuf->flag &= ~RB_NFLUSHED; + set_breakpoint(obuf, 0); + obuf->prev_ctype = PC_ASCII; + link_stack = NULL; + fillline(obuf, indent); + if (pass) + passthrough(obuf, pass->ptr, 0); + if (!hidden_anchor && obuf->anchor) { + Str tmp; + if (obuf->anchor_hseq > 0) + obuf->anchor_hseq = -obuf->anchor_hseq; + tmp = Sprintf("<A HSEQ=\"%d\" HREF=\"", obuf->anchor_hseq); + Strcat_charp(tmp, htmlquote_str(obuf->anchor->ptr)); + if (obuf->anchor_target) { + Strcat_charp(tmp, "\" TARGET=\""); + Strcat_charp(tmp, htmlquote_str(obuf->anchor_target->ptr)); + } + Strcat_charp(tmp, "\">"); + push_tag(obuf, tmp->ptr, HTML_A); + } + if (!hidden_img && obuf->img_alt) { + Str tmp = Strnew_charp("<IMG_ALT SRC=\""); + Strcat_charp(tmp, htmlquote_str(obuf->img_alt->ptr)); + Strcat_charp(tmp, "\">"); + push_tag(obuf, tmp->ptr, HTML_IMG_ALT); + } + if (!hidden_bold && obuf->in_bold) + push_tag(obuf, "<B>", HTML_B); + if (!hidden_under && obuf->in_under) + push_tag(obuf, "<U>", HTML_U); +} + +static void +discardline(struct readbuffer *obuf, int indent) +{ + append_tags(obuf); + Strclear(obuf->line); + obuf->pos = 0; + obuf->prevchar = ' '; + obuf->bp.init_flag = 1; + set_breakpoint(obuf, 0); + obuf->prev_ctype = PC_ASCII; + fillline(obuf, indent); +} + +void +do_blankline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent, int indent_incr, int width) +{ + if (h_env->buf && h_env->blank_lines == 0) + flushline(h_env, obuf, indent, 1, width); + else if (h_env->f) + flushline(h_env, obuf, indent, 1, width); +} + +void +purgeline(struct html_feed_environ *h_env) +{ + char *p, *q; + Str tmp; + + if (h_env->buf == NULL || h_env->blank_lines == 0) + return; + + p = rpopTextLine(h_env->buf)->line->ptr; + tmp = Strnew(); + while (*p) { + q = p; + if (sloppy_parse_line(&p)) { + Strcat_charp_n(tmp, q, p - q); + appendTextLine(h_env->buf ,tmp, 0); + } + } + h_env->blank_lines--; +} + +static int +close_effect0(struct readbuffer *obuf, int cmd) +{ + int i; + char *p; + + for (i = obuf->tag_sp - 1; i >= 0; i--) { + if (obuf->tag_stack[i]->cmd == cmd) + break; + } + if (i >= 0) { + obuf->tag_sp--; + bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i], + (obuf->tag_sp - i) * sizeof(struct cmdtable *)); + return 1; + } + else if ((p = has_hidden_link(obuf, cmd)) != NULL) { + passthrough(obuf, p, 1); + return 1; + } + return 0; +} + +static void +close_anchor(struct html_feed_environ *h_env, struct readbuffer *obuf) +{ + if (obuf->anchor) { + int i; + char *p = NULL; + int is_erased = 0; + + for (i = obuf->tag_sp - 1; i >= 0; i--) { + if (obuf->tag_stack[i]->cmd == HTML_A) + break; + } + if (i < 0 && obuf->anchor_hseq > 0 && + Strlastchar(obuf->line) == ' ') { + Strshrink(obuf->line, 1); + obuf->pos--; + is_erased = 1; + } + + if (i >= 0 || (p = has_hidden_link(obuf, HTML_A))) { + if (obuf->anchor_hseq > 0) { + HTMLlineproc1(ANSP, h_env); + obuf->prevchar = ' '; + } + else { + if (i >= 0) { + obuf->tag_sp--; + bcopy(&obuf->tag_stack[i + 1], &obuf->tag_stack[i], + (obuf->tag_sp - i) * sizeof(struct cmdtable *)); + } + else { + passthrough(obuf, p, 1); + } + obuf->anchor = NULL; + obuf->anchor_target = NULL; + return; + } + is_erased = 0; + } + if (is_erased) { + Strcat_char(obuf->line, ' '); + obuf->pos++; + } + + push_tag(obuf, "</a>", HTML_N_A); + obuf->anchor = NULL; + } + obuf->anchor_target = NULL; +} + +void +save_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf) +{ + if (obuf->fontstat_sp < FONT_STACK_SIZE) + bcopy(obuf->fontstat, obuf->fontstat_stack[obuf->fontstat_sp], + FONTSTAT_SIZE); + obuf->fontstat_sp++; + if (obuf->in_bold) + push_tag(obuf, "</b>", HTML_N_B); + if (obuf->in_under) + push_tag(obuf, "</u>", HTML_N_U); + bzero(obuf->fontstat, FONTSTAT_SIZE); +} + +void +restore_fonteffect(struct html_feed_environ *h_env, struct readbuffer *obuf) +{ + if (obuf->fontstat_sp > 0) + obuf->fontstat_sp--; + if (obuf->fontstat_sp < FONT_STACK_SIZE) + bcopy(obuf->fontstat_stack[obuf->fontstat_sp], obuf->fontstat, + FONTSTAT_SIZE); + if (obuf->in_bold) + push_tag(obuf, "<b>", HTML_B); + if (obuf->in_under) + push_tag(obuf, "<u>", HTML_U); +} + + +Str +process_img(struct parsed_tag * tag) +{ + char *p, *q, *r, *r2, *s; + int w, i; + Str tmp = Strnew(); + + if (!parsedtag_get_value(tag, ATTR_SRC, &p)) + return tmp; + q = NULL; + parsedtag_get_value(tag, ATTR_ALT, &q); + w = -1; + parsedtag_get_value(tag, ATTR_WIDTH, &w); + i = -1; + parsedtag_get_value(tag, ATTR_HEIGHT, &i); + r = NULL; + parsedtag_get_value(tag, ATTR_USEMAP, &r); + + tmp = Strnew_size(128); + if (r) { + r2 = strchr(r, '#'); +#ifdef NEW_FORM + s = "<form_int method=internal action=map>"; + process_form(parse_tag(&s, TRUE)); + Strcat(tmp, Sprintf("<pre_int><input_alt fid=\"%d\" " + "type=hidden name=link value=\"", + cur_form_id)); + Strcat_charp(tmp, htmlquote_str((r2) ? r2 + 1 : r)); + Strcat(tmp, Sprintf("\"><input_alt hseq=\"%d\" fid=\"%d\" " + "type=submit no_effect=true>", + cur_hseq++, cur_form_id)); +#else /* not NEW_FORM */ + Strcat_charp(tmp, "<form_int method=internal action=map>" + "<pre_int><input_alt type=hidden name=link value=\""); + Strcat_charp(tmp, htmlquote_str((r2) ? r2 + 1 : r)); + Strcat(tmp, Sprintf("\"><input_alt hseq=\"%d\" type=submit " + "no_effect=true>", cur_hseq++)); +#endif /* not NEW_FORM */ + } + if (q != NULL && *q == '\0' && ignore_null_img_alt) + q = NULL; + if (q != NULL || r != NULL) + Strcat_charp(tmp, "<img_alt src=\""); + else + Strcat_charp(tmp, "<nobr><img_alt src=\""); + Strcat_charp(tmp, htmlquote_str(p)); + Strcat_charp(tmp, "\">"); + if (q != NULL) { + Strcat_charp(tmp, htmlquote_str(q)); + Strcat_charp(tmp, "</img_alt> "); + goto img_end2; + } + if (w > 0 && i > 0) { + /* guess what the image is! */ + if (w < 32 && i < 48) { + /* must be an icon or space */ + if (strcasestr(p, "space") || strcasestr(p, "blank")) + Strcat_charp(tmp, "_</img_alt>"); + else { + if (w * i < 8 * 16) + Strcat_charp(tmp, "*</img_alt>"); + else { +#ifdef KANJI_SYMBOLS + Strcat_charp(tmp, "��</img_alt>"); +#else /* not KANJI_SYMBOLS */ + Strcat_charp(tmp, "#</img_alt>"); +#endif /* not KANJI_SYMBOLS */ + } + } + goto img_end1; + } + if (w > 200 && i < 13) { + /* must be a horizontal line */ +#ifndef KANJI_SYMBOLS + Strcat_charp(tmp, "<_RULE>"); +#endif /* not KANJI_SYMBOLS */ + w /= pixel_per_char; + for (i = 0; i < w - (HR_RULE_WIDTH - 1); i += HR_RULE_WIDTH) + Strcat_charp(tmp, HR_RULE); +#ifndef KANJI_SYMBOLS + Strcat_charp(tmp, "</_RULE>"); +#endif /* not KANJI_SYMBOLS */ + Strcat_charp(tmp, "</img_alt>"); + goto img_end1; + } + } + for (q = p; *q; q++); + while (q > p && *q != '/') + q--; + if (*q == '/') + q++; + Strcat_char(tmp, '['); + p = q; + for (; *q; q++) { + if (!IS_ALNUM(*q) && *q != '_' && *q != '-') { + break; + } + else if (w > 0 && !IS_ALNUM(*q) && q - p + 2 > (int)(w / pixel_per_char)) { + Strcat_charp(tmp, ".."); + break; + } + Strcat_char(tmp, *q); + } + Strcat_charp(tmp, "]</img_alt>"); + img_end1: + if (r == NULL) + Strcat_charp(tmp, "</nobr>"); + img_end2: + if (r) { +#ifdef NEW_FORM + Strcat_charp(tmp, "</input_alt></pre_int>"); + process_n_form(); +#else /* not NEW_FORM */ + Strcat_charp(tmp, "</input_alt></pre_int></form_int>"); +#endif /* not NEW_FORM */ + } + return tmp; +} + +Str +process_anchor(struct parsed_tag * tag, char *tagbuf) +{ + if (parsedtag_need_reconstruct(tag)) { + parsedtag_set_value(tag, ATTR_HSEQ, Sprintf("%d", cur_hseq++)->ptr); + return parsedtag2str(tag); + } + else { + Str tmp = Sprintf("<a hseq=\"%d\"", cur_hseq++); + Strcat_charp(tmp, tagbuf + 2); + return tmp; + } +} + +Str +process_input(struct parsed_tag * tag) +{ + int i, w, v, x, y; + char *q , *p, *r, *p2; + Str tmp; + char *qq = ""; + int qlen = 0; + + p = "text"; + parsedtag_get_value(tag, ATTR_TYPE, &p); + q = NULL; + parsedtag_get_value(tag, ATTR_VALUE, &q); + r = ""; + parsedtag_get_value(tag, ATTR_NAME, &r); + w = 20; + parsedtag_get_value(tag, ATTR_SIZE, &w); + i = 20; + parsedtag_get_value(tag, ATTR_MAXLENGTH, &i); + p2 = NULL; + parsedtag_get_value(tag, ATTR_ALT, &p2); + x = parsedtag_exists(tag, ATTR_CHECKED); + y = parsedtag_exists(tag, ATTR_ACCEPT); + + v = formtype(p); + if (v == FORM_UNKNOWN) + return NULL; + + if (!q) { + switch (v) { + case FORM_INPUT_IMAGE: + case FORM_INPUT_SUBMIT: + case FORM_INPUT_BUTTON: + q = "SUBMIT"; + break; + case FORM_INPUT_RESET: + q = "RESET"; + break; + /* if no VALUE attribute is specified in * <INPUT + * TYPE=CHECHBOX> tag, then the value "on" is used * as a + * default value. It is not a part of HTML4.0 * specification, + * but an imitation of Netscape * behaviour. */ + case FORM_INPUT_CHECKBOX: + q = "on"; + } + } + /* VALUE attribute is not allowed in <INPUT TYPE=FILE> tag. */ + if (v == FORM_INPUT_FILE) + q = NULL; + if (q) { + qq = htmlquote_str(q); + qlen = strlen(q); + } + + tmp = Strnew_charp("<pre_int>"); + switch (v) { + case FORM_INPUT_PASSWORD: + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_CHECKBOX: + Strcat_char(tmp, '['); + break; + case FORM_INPUT_RADIO: + Strcat_char(tmp, '('); + } +#ifdef NEW_FORM + Strcat(tmp, Sprintf("<input_alt hseq=\"%d\" fid=\"%d\" type=%s " + "name=\"%s\" width=%d maxlength=%d value=\"%s\"", + cur_hseq++, cur_form_id, + p, htmlquote_str(r), w, i, qq)); +#else /* not NEW_FORM */ + Strcat(tmp, Sprintf("<input_alt hseq=\"%d\" type=%s " + "name=\"%s\" width=%d maxlength=%d value=\"%s\"", + cur_hseq++, p, htmlquote_str(r), w, i, qq)); +#endif /* not NEW_FORM */ + if (x) + Strcat_charp(tmp, " checked"); + if (y) + Strcat_charp(tmp, " accept"); + Strcat_char(tmp, '>'); + + if (v == FORM_INPUT_HIDDEN) + Strcat_charp(tmp, "</input_alt></pre_int>"); + else { + switch (v) { + case FORM_INPUT_PASSWORD: + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + Strcat_charp(tmp, "<u>"); + break; + case FORM_INPUT_IMAGE: + case FORM_INPUT_SUBMIT: + case FORM_INPUT_BUTTON: + case FORM_INPUT_RESET: + Strcat_charp(tmp, "["); + break; + } + switch (v) { + case FORM_INPUT_PASSWORD: + i = 0; + if (q) { + for (; i < qlen && i < w; i++) + Strcat_char(tmp, '*'); + } + for (; i < w; i++) + Strcat_char(tmp, ' '); + break; + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + if (q) + Strcat(tmp, textfieldrep(Strnew_charp(q), w)); + else { + for (i = 0; i < w; i++) + Strcat_char(tmp, ' '); + } + break; + case FORM_INPUT_IMAGE: + case FORM_INPUT_SUBMIT: + case FORM_INPUT_BUTTON: + if (p2) { + Strcat_charp(tmp, htmlquote_str(p2)); + i = strlen(p2); + } + else { + Strcat_charp(tmp, qq); + i = qlen; + } + break; + case FORM_INPUT_RESET: + Strcat_charp(tmp, qq); + i = qlen; + break; + case FORM_INPUT_RADIO: + case FORM_INPUT_CHECKBOX: + if (x) + Strcat_char(tmp, '*'); + else + Strcat_char(tmp, ' '); + break; + } + switch (v) { + case FORM_INPUT_PASSWORD: + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + Strcat_charp(tmp, "</u>"); + break; + case FORM_INPUT_IMAGE: + case FORM_INPUT_SUBMIT: + case FORM_INPUT_BUTTON: + case FORM_INPUT_RESET: + Strcat_charp(tmp, "]"); + } + Strcat_charp(tmp, "</input_alt>"); + switch (v) { + case FORM_INPUT_PASSWORD: + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_CHECKBOX: + Strcat_char(tmp, ']'); + break; + case FORM_INPUT_RADIO: + Strcat_char(tmp, ')'); + } + Strcat_charp(tmp, "</pre_int>"); + } + return tmp; +} + +void +process_select(struct parsed_tag * tag) +{ + char *p; + + p = ""; + parsedtag_get_value(tag, ATTR_NAME, &p); + cur_select = Strnew_charp(p); + select_is_multiple = parsedtag_exists(tag, ATTR_MULTIPLE); + +#ifdef MENU_SELECT + if (!select_is_multiple && n_select < MAX_SELECT) { +#ifdef NEW_FORM + select_str = Sprintf("<pre_int>[<input_alt hseq=\"%d\" " + "fid=\"%d\" type=select name=\"%s\" selectnumber=%d", + cur_hseq++, cur_form_id, htmlquote_str(p), n_select); +#else /* not NEW_FORM */ + select_str = Sprintf("<pre_int><input_alt hseq=\"%d\" " + "type=select name=\"%s\" selectnumber=%d", + cur_hseq++, htmlquote_str(p), n_select); +#endif /* not NEW_FORM */ + Strcat_charp(select_str, ">"); + select_option[n_select].first = NULL; + select_option[n_select].last = NULL; + cur_option_maxwidth = 0; + } else +#endif /* MENU_SELECT */ + select_str = Strnew(); + cur_option = NULL; + cur_status = R_ST_NORMAL; + n_selectitem = 0; +} + +Str +process_n_select(void) +{ + if (cur_select == NULL) + return NULL; + process_option(); +#ifdef MENU_SELECT + if (!select_is_multiple && n_select < MAX_SELECT) { + if (select_option[n_select].first) + Strcat(select_str, textfieldrep(chooseSelectOption( + select_option[n_select].first, CHOOSE_OPTION), + cur_option_maxwidth)); + Strcat_charp(select_str, "</input_alt>]</pre_int>"); + n_select++; + if (n_select == MAX_SELECT) { + disp_err_message("Too many select in one page", FALSE); + } + } else +#endif /* MENU_SELECT */ + Strcat_charp(select_str, "<br>"); + cur_select = NULL; + n_selectitem = 0; + return select_str; +} + +void +feed_select(char *str) +{ + Str tmp = Strnew(); + int prev_status = cur_status; + static int prev_spaces = -1; + char *p; + + if (cur_select == NULL) + return; + while (read_token(tmp, &str, &cur_status, 0, 0)) { + if (cur_status != R_ST_NORMAL || + prev_status != R_ST_NORMAL) + continue; + p = tmp->ptr; + if (tmp->ptr[0] == '<' && Strlastchar(tmp) == '>') { + struct parsed_tag *tag; + char *q; + if (!(tag = parse_tag(&p, FALSE))) + continue; + switch (tag->tagid) { + case HTML_OPTION: + process_option(); + cur_option = Strnew(); + if (parsedtag_get_value(tag, ATTR_VALUE, &q)) + cur_option_value = Strnew_charp(q); + else + cur_option_value = NULL; + if (parsedtag_get_value(tag, ATTR_LABEL, &q)) + cur_option_label = Strnew_charp(q); + else + cur_option_label = NULL; + cur_option_selected = parsedtag_exists(tag, ATTR_SELECTED); + prev_spaces = -1; + break; + case HTML_N_OPTION: + /* do nothing */ + break; + default: + /* never happen */ + break; + } + } + else if (cur_option) { + while (*p) { + if (IS_SPACE(*p) && prev_spaces != 0) { + p++; + if (prev_spaces > 0) + prev_spaces++; + } + else { + if (IS_SPACE(*p)) + prev_spaces = 1; + else + prev_spaces = 0; + if (*p == '&') + Strcat_charp(cur_option, getescapecmd(&p)); + else + Strcat_char(cur_option, *(p++)); + } + } + } + } +} + +void +process_option(void) +{ + char begin_char = '[', end_char = ']'; + + if (cur_select == NULL || cur_option == NULL) + return; + while (cur_option->length > 0 && IS_SPACE(Strlastchar(cur_option))) + Strshrink(cur_option, 1); + if (cur_option_value == NULL) + cur_option_value = cur_option; + if (cur_option_label == NULL) + cur_option_label = cur_option; +#ifdef MENU_SELECT + if (!select_is_multiple && n_select < MAX_SELECT) { + if (cur_option_label->length > cur_option_maxwidth) + cur_option_maxwidth = cur_option_label->length; + addSelectOption(&select_option[n_select], + cur_option_value, + cur_option_label, + cur_option_selected); + return; + } +#endif /* MENU_SELECT */ +#ifdef NEW_FORM + if (!select_is_multiple) { + begin_char = '('; + end_char = ')'; + } + Strcat(select_str, Sprintf("<br><pre_int>%c<input_alt hseq=\"%d\" " + "fid=\"%d\" type=%s name=\"%s\" value=\"%s\"", + begin_char, cur_hseq++, cur_form_id, +#else /* not NEW_FORM */ + Strcat(select_str, Sprintf("<br><pre_int><input_alt hseq=\"%d\" " + "type=%s name=\"%s\" value=\"%s\"", + cur_hseq++, +#endif /* not NEW_FORM */ + select_is_multiple ? "checkbox" : "radio", + htmlquote_str(cur_select->ptr), + htmlquote_str(cur_option_value->ptr))); + if (cur_option_selected) + Strcat_charp(select_str, " checked>*</input_alt>"); + else + Strcat_charp(select_str, "> </input_alt>"); +#ifdef NEW_FORM + Strcat_char(select_str, end_char); +#endif /* not NEW_FORM */ + Strcat_charp(select_str, htmlquote_str(cur_option_label->ptr)); + Strcat_charp(select_str, "</pre_int>"); + n_selectitem++; +} + +Str +process_textarea(struct parsed_tag * tag, int width) +{ + char *p, *q, *r; + + p = "40"; + parsedtag_get_value(tag, ATTR_COLS, &p); + q = ""; + parsedtag_get_value(tag, ATTR_NAME, &q); + r = "10"; + parsedtag_get_value(tag, ATTR_ROWS, &r); + cur_textarea = Strnew_charp(q); + cur_textarea_size = atoi(p); + if (p[strlen(p) - 1] == '%') { + cur_textarea_size = width * cur_textarea_size / 100 - 2; + } + if (cur_textarea_size <= 0) + cur_textarea_size = 40; + + cur_textarea_rows = atoi(r); + if (n_textarea < MAX_TEXTAREA) + textarea_str[n_textarea] = Strnew(); + + return NULL; +} + +Str +process_n_textarea(void) +{ + Str tmp; + + if (cur_textarea == NULL || n_textarea >= MAX_TEXTAREA) + return NULL; + +#ifdef NEW_FORM + tmp = Sprintf("<pre_int>[<input_alt hseq=\"%d\" fid=\"%d\" type=textarea " + "name=\"%s\" size=%d rows=%d textareanumber=%d><u>", + cur_hseq++, cur_form_id, htmlquote_str(cur_textarea->ptr), + cur_textarea_size, cur_textarea_rows, n_textarea); +#else /* not NEW_FORM */ + tmp = Sprintf("<pre_int>[<input_alt hseq=\"%d\" type=textarea " + "name=\"%s\" size=%d rows=%d textareanumber=%d><u>", + cur_hseq++, htmlquote_str(cur_textarea->ptr), + cur_textarea_size, cur_textarea_rows, n_textarea); +#endif /* not NEW_FORM */ + Strcat(tmp, textfieldrep(textarea_str[n_textarea], cur_textarea_size)); + Strcat_charp(tmp, "</u></input_alt>]</pre_int>"); + n_textarea++; + if (n_textarea == MAX_TEXTAREA) { + disp_err_message("Too many textarea in one page", FALSE); + } + else + textarea_str[n_textarea] = Strnew(); + cur_textarea = NULL; + + return tmp; +} + +void +feed_textarea(char *str) +{ + if (n_textarea < MAX_TEXTAREA) { + while (*str) { + if (*str == '&') + Strcat_charp(textarea_str[n_textarea], getescapecmd(&str)); + else if (*str == '\n') { + Strcat_charp(textarea_str[n_textarea], "\r\n"); + str++; + } + else + Strcat_char(textarea_str[n_textarea], *(str++)); + } + } +} + +Str +process_hr(struct parsed_tag *tag, int width, int indent_width) +{ + Str tmp = Strnew_charp("<nobr>"); + int i,w = 0; + int x = ALIGN_CENTER; + + if (width > indent_width) + width -= indent_width; + if (parsedtag_get_value(tag, ATTR_WIDTH, &w)) + w = REAL_WIDTH(w, width); + else + w = width; + + parsedtag_get_value(tag, ATTR_ALIGN, &x); + switch (x) { + case ALIGN_CENTER: + Strcat_charp(tmp,"<div align=center>"); + break; + case ALIGN_RIGHT: + Strcat_charp(tmp,"<div align=right>"); + break; + case ALIGN_LEFT: + Strcat_charp(tmp,"<div align=left>"); + break; + } +#ifndef KANJI_SYMBOLS + Strcat_charp(tmp, "<_RULE>"); +#endif /* not KANJI_SYMBOLS */ + w -= HR_RULE_WIDTH - 1; + if (w <= 0) + w = 1; + for (i = 0; i < w; i += HR_RULE_WIDTH) { + Strcat_charp(tmp, HR_RULE); + } +#ifndef KANJI_SYMBOLS + Strcat_charp(tmp, "</_RULE>"); +#endif /* not KANJI_SYMBOLS */ + Strcat_charp(tmp,"</div></nobr>"); + return tmp; +} + +#ifdef NEW_FORM +#ifdef JP_CHARSET +char +check_charset(char *s) +{ + switch (*s) { + case CODE_EUC: + case CODE_SJIS: + case CODE_JIS_n: + case CODE_JIS_m: + case CODE_JIS_N: + case CODE_JIS_j: + case CODE_JIS_J: + case CODE_INNER_EUC: + return *s; + } + return 0; +} + +char +check_accept_charset(char *s) +{ + char *e; + char c; + + while (*s) { + while (*s && (IS_SPACE(*s) || *s == ',')) + s++; + if (!*s) + break; + e = s; + while (*e && !(IS_SPACE(*e) || *e == ',')) + e++; + c = guess_charset(Strnew_charp_n(s, e - s)->ptr); + if (c) + return c; + s = e; + } + return 0; +} +#endif + +Str +process_form(struct parsed_tag *tag) +{ + char *p, *q, *r, *s, *tg; + char cs = 0; + + p = "get"; + parsedtag_get_value(tag, ATTR_METHOD, &p); + q = "!CURRENT_URL!"; + parsedtag_get_value(tag, ATTR_ACTION, &q); + r = NULL; +#ifdef JP_CHARSET + if (parsedtag_get_value(tag, ATTR_ACCEPT_CHARSET, &r)) + cs = check_accept_charset(r); + if (! cs && parsedtag_get_value(tag, ATTR_CHARSET, &r)) + cs = check_charset(r); +#endif + s = NULL; + parsedtag_get_value(tag, ATTR_ENCTYPE, &s); + tg = NULL; + parsedtag_get_value(tag, ATTR_TARGET, &tg); + form_max++; + form_sp++; + if (forms_size == 0) { + forms_size = INITIAL_FORM_SIZE; + forms = New_N(FormList *, forms_size); + form_stack = New_N(int, forms_size); + } + else if (forms_size <= form_max) { + forms_size += form_max; + forms = New_Reuse(FormList *, forms, forms_size); + form_stack = New_Reuse(int, form_stack, forms_size); + } + forms[form_max] = + newFormList(q, p, &cs, s, tg, (form_max > 0) ? forms[form_max - 1] : NULL); + form_stack[form_sp] = form_max; + + return NULL; +} + +Str +process_n_form(void) +{ + if (form_sp >= 0) + form_sp--; + return NULL; +} +#endif /* NEW_FORM */ + +static void +clear_ignore_p_flag(int cmd, struct readbuffer *obuf) +{ + static int clear_flag_cmd[] = + { + HTML_HR, HTML_UNKNOWN + }; + int i; + + for (i = 0; clear_flag_cmd[i] != HTML_UNKNOWN; i++) { + if (cmd == clear_flag_cmd[i]) { + obuf->flag &= ~RB_IGNORE_P; + return; + } + } +} + +static void +set_alignment(struct readbuffer *obuf, struct parsed_tag *tag) +{ + long flag = -1; + int align; + + if (parsedtag_get_value(tag, ATTR_ALIGN, &align)) { + switch (align) { + case ALIGN_CENTER: + flag = RB_CENTER; + break; + case ALIGN_RIGHT: + flag = RB_RIGHT; + break; + case ALIGN_LEFT: + flag = RB_LEFT; + } + } + RB_SAVE_FLAG(obuf); + if (flag != -1) { + RB_SET_ALIGN(obuf, flag); + } +} + +#ifdef ID_EXT +static void +process_idattr(struct readbuffer *obuf, int cmd, struct parsed_tag *tag) +{ + char *id = NULL, *framename = NULL; + Str idtag = NULL; + + /* + * HTML_TABLE is handled by the other process. + */ + if (cmd == HTML_TABLE) + return; + + parsedtag_get_value(tag, ATTR_ID, &id); + parsedtag_get_value(tag, ATTR_FRAMENAME, &framename); + if (id == NULL) + return; + if (framename) + idtag = Sprintf("<_id id=\"%s\" framename=\"%s\">", + htmlquote_str(id), htmlquote_str(framename)); + else + idtag = Sprintf("<_id id=\"%s\">", htmlquote_str(id)); + push_tag(obuf, idtag->ptr, HTML_NOP); +} +#endif /* ID_EXT */ + +#define CLOSE_P if (obuf->flag & RB_P) { \ + flushline(h_env, obuf, envs[h_env->envc].indent,0,h_env->limit);\ + RB_RESTORE_FLAG(obuf);\ + close_anchor(h_env, obuf);\ + obuf->flag &= ~RB_P;\ + } + +#define CLOSE_DT \ + if (obuf->flag & RB_IN_DT) { \ + obuf->flag &= ~RB_IN_DT; \ + HTMLlineproc1("</b>", h_env); \ + } + +#define PUSH_ENV(cmd) \ + if (++h_env->envc_real < h_env->nenv) { \ + ++h_env->envc; \ + envs[h_env->envc].env = cmd; \ + envs[h_env->envc].count = 0; \ + if (h_env->envc <= MAX_INDENT_LEVEL) \ + envs[h_env->envc].indent = envs[h_env->envc - 1].indent + INDENT_INCR; \ + else \ + envs[h_env->envc].indent = envs[h_env->envc - 1].indent; \ + } + +#define POP_ENV \ + if (h_env->envc_real-- < h_env->nenv) \ + h_env->envc--; + +static int +ul_type(struct parsed_tag *tag, int default_type) +{ + char *p; + if (parsedtag_get_value(tag, ATTR_TYPE, &p)) { + if (!strcasecmp(p, "disc")) + return (int) 'd'; + else if (!strcasecmp(p, "circle")) + return (int) 'c'; + else if (!strcasecmp(p, "square")) + return (int) 's'; + } + return default_type; +} + +int +HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env) +{ + char *p, *q, *r; + int i, w, x, y, z, count, width; + struct readbuffer *obuf = h_env->obuf; + struct environment *envs = h_env->envs; + Str tmp; + int hseq; + int cmd; +#ifdef ID_EXT + char *id = NULL; +#endif /* ID_EXT */ + + cmd = tag->tagid; + + if (obuf->flag & RB_PRE) { + switch (cmd) { + case HTML_NOBR: + case HTML_N_NOBR: + case HTML_PRE_INT: + case HTML_N_PRE_INT: + return 1; + } + } + + switch (cmd) { + case HTML_B: + obuf->in_bold++; + if (obuf->in_bold > 1) + return 1; + return 0; + case HTML_N_B: + if (obuf->in_bold == 1 && close_effect0(obuf, HTML_B)) + obuf->in_bold = 0; + if (obuf->in_bold > 0) { + obuf->in_bold--; + if (obuf->in_bold == 0) + return 0; + } + return 1; + case HTML_U: + obuf->in_under++; + if (obuf->in_under > 1) + return 1; + return 0; + case HTML_N_U: + if (obuf->in_under == 1 && close_effect0(obuf, HTML_U)) + obuf->in_under = 0; + if (obuf->in_under > 0) { + obuf->in_under--; + if (obuf->in_under == 0) + return 0; + } + return 1; + case HTML_EM: + HTMLlineproc1("<b>", h_env); + return 1; + case HTML_N_EM: + HTMLlineproc1("</b>", h_env); + return 1; + case HTML_P: + case HTML_N_P: + CLOSE_P; + if (!(obuf->flag & RB_IGNORE_P)) { + flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit); + do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + obuf->flag |= RB_IGNORE_P; + if (cmd == HTML_P) { + set_alignment(obuf, tag); + obuf->flag |= RB_P; + } + return 1; + case HTML_BR: + flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit); + h_env->blank_lines = 0; + return 1; + case HTML_EOL: + if ((obuf->flag & RB_PREMODE) && obuf->pos > envs[h_env->envc].indent) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + return 1; + case HTML_H: + if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P))) { + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + HTMLlineproc1("<b>", h_env); + set_alignment(obuf, tag); + return 1; + case HTML_N_H: + HTMLlineproc1("</b>", h_env); + if (!(obuf->flag & RB_PREMODE)) { + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + RB_RESTORE_FLAG(obuf); + close_anchor(h_env, obuf); + obuf->flag |= RB_IGNORE_P; + return 1; + case HTML_UL: + case HTML_OL: + case HTML_BLQ: + CLOSE_P; + if (!(obuf->flag & RB_IGNORE_P)) { + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + if (!(obuf->flag & RB_PREMODE) && + (h_env->envc == 0 || cmd == HTML_BLQ)) + do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + PUSH_ENV(cmd); + if (cmd == HTML_UL || cmd == HTML_OL) { + if (parsedtag_get_value(tag, ATTR_START, &count) && + count > 0) { + envs[h_env->envc].count = count - 1; + } + } + if (cmd == HTML_OL) { + envs[h_env->envc].type = '1'; + if (parsedtag_get_value(tag, ATTR_TYPE, &p)) { + envs[h_env->envc].type = (int) *p; + } + } + if (cmd == HTML_UL) + envs[h_env->envc].type = ul_type(tag, 0); + if (cmd == HTML_BLQ) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + return 1; + case HTML_N_UL: + case HTML_N_OL: + case HTML_N_DL: + case HTML_N_BLQ: + CLOSE_DT; + CLOSE_P; + if (h_env->envc > 0) { + flushline(h_env, obuf, envs[h_env->envc - 1].indent, 0, h_env->limit); + POP_ENV; + if (!(obuf->flag & RB_PREMODE) && + (h_env->envc == 0 || cmd == HTML_N_DL || cmd == HTML_N_BLQ)) { + do_blankline(h_env, obuf, + envs[h_env->envc].indent, + INDENT_INCR, + h_env->limit); + obuf->flag |= RB_IGNORE_P; + } + } + close_anchor(h_env, obuf); + return 1; + case HTML_DL: + CLOSE_P; + if (!(obuf->flag & RB_IGNORE_P)) { + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + if (!(obuf->flag & RB_PREMODE)) + do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + PUSH_ENV(cmd); + if (parsedtag_exists(tag, ATTR_COMPACT)) + envs[h_env->envc].env = HTML_DL_COMPACT; + obuf->flag |= RB_IGNORE_P; + return 1; + case HTML_LI: + CLOSE_P; + CLOSE_DT; + if (h_env->envc > 0) { + Str num; + flushline(h_env, obuf, + envs[h_env->envc - 1].indent, 0, h_env->limit); + envs[h_env->envc].count++; + if (parsedtag_get_value(tag, ATTR_VALUE, &p)) { + count = atoi(p); + if (count > 0) + envs[h_env->envc].count = count; + } + switch (envs[h_env->envc].env) { + case HTML_UL: + envs[h_env->envc].type = ul_type(tag, envs[h_env->envc].type); + for (i = 0; i < INDENT_INCR - 3; i++) + push_charp(obuf, 1, NBSP, PC_ASCII); + switch (envs[h_env->envc].type) { +#ifdef KANJI_SYMBOLS + case 'd': + push_charp(obuf, 2, "��", PC_ASCII); + break; + case 'c': + push_charp(obuf, 2, "��", PC_ASCII); + break; + case 's': + push_charp(obuf, 2, "��", PC_ASCII); + break; +#endif /* KANJI_SYMBOLS */ + default: + push_charp(obuf, 2, ullevel[(h_env->envc_real - 1) % MAX_UL_LEVEL], PC_ASCII); + break; + } + push_charp(obuf, 1, NBSP, PC_ASCII); + obuf->prevchar = ' '; + break; + case HTML_OL: + if (parsedtag_get_value(tag, ATTR_TYPE, &p)) + envs[h_env->envc].type = (int) *p; + switch (envs[h_env->envc].type) { + case 'i': + num = romanNumeral(envs[h_env->envc].count); + break; + case 'I': + num = romanNumeral(envs[h_env->envc].count); + Strupper(num); + break; + case 'a': + num = romanAlphabet(envs[h_env->envc].count); + break; + case 'A': + num = romanAlphabet(envs[h_env->envc].count); + Strupper(num); + break; + default: + num = Sprintf("%d", envs[h_env->envc].count); + break; + } +#if INDENT_INCR >= 4 + Strcat_charp(num, ". "); +#else /* INDENT_INCR < 4 */ + Strcat_char(num, '.'); +#endif /* INDENT_INCR < 4 */ + push_spaces(obuf, 1, INDENT_INCR - num->length); + push_str(obuf, num->length, num, PC_ASCII); + break; + default: + push_spaces(obuf, 1, INDENT_INCR); + break; + } + } + else { + flushline(h_env, obuf, 0, 0, h_env->limit); + } + obuf->flag |= RB_IGNORE_P; + return 1; + case HTML_DT: + CLOSE_P; + if (h_env->envc == 0 || + (h_env->envc_real < h_env->nenv && + envs[h_env->envc].env != HTML_DL && + envs[h_env->envc].env != HTML_DL_COMPACT)) { + PUSH_ENV(HTML_DL); + } + if (h_env->envc > 0) { + flushline(h_env, obuf, + envs[h_env->envc - 1].indent, 0, h_env->limit); + } + if (!(obuf->flag & RB_IN_DT)) { + HTMLlineproc1("<b>", h_env); + obuf->flag |= RB_IN_DT; + } + obuf->flag |= RB_IGNORE_P; + return 1; + case HTML_DD: + CLOSE_P; + CLOSE_DT; + if (envs[h_env->envc].env == HTML_DL_COMPACT) { + if (obuf->pos > envs[h_env->envc].indent) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + else + push_spaces(obuf, 1, envs[h_env->envc].indent - obuf->pos); + } + else + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + /* obuf->flag |= RB_IGNORE_P; */ + return 1; + case HTML_TITLE: + append_tags(obuf); + save_line = obuf->line; + save_prevchar = obuf->prevchar; + set_breakpoint(obuf, 0); + obuf->line = Strnew(); + discardline(obuf, 0); + obuf->flag |= (RB_NOBR | RB_TITLE); + return 1; + case HTML_N_TITLE: + if (!(obuf->flag & RB_TITLE)) + return 1; + obuf->flag &= ~(RB_NOBR | RB_TITLE); + append_tags(obuf); + tmp = Strnew_charp(obuf->line->ptr); + Strremovetrailingspaces(tmp); + h_env->title = cleanup_str(tmp->ptr); + obuf->line = save_line; + obuf->prevchar = save_prevchar; + back_to_breakpoint(obuf); + tmp = Strnew_m_charp( + "<title_alt title=\"", + htmlquote_str(h_env->title), + "\">", + NULL); + push_tag(obuf, tmp->ptr, HTML_TITLE_ALT); + return 1; + case HTML_FRAMESET: + PUSH_ENV(cmd); + push_charp(obuf, 9, "--FRAME--", PC_ASCII); + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + return 0; + case HTML_N_FRAMESET: + if (h_env->envc > 0) { + POP_ENV; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + } + return 0; + case HTML_FRAME: + q = r = NULL; + parsedtag_get_value(tag, ATTR_SRC, &q); + parsedtag_get_value(tag, ATTR_NAME, &r); + if (q) { + q = htmlquote_str(q); + push_tag(obuf, Sprintf("<a hseq=\"%d\" href=\"%s\">", + cur_hseq++, q)->ptr, HTML_A); + if (r) + q = htmlquote_str(r); + push_charp(obuf, strlen(q), q, PC_ASCII); + push_tag(obuf, "</a>", HTML_N_A); + } + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + return 0; + case HTML_HR: + tmp = process_hr(tag,h_env->limit,envs[h_env->envc].indent); + HTMLlineproc1(tmp->ptr,h_env); + obuf->prevchar = ' '; + close_anchor(h_env, obuf); + return 1; + case HTML_PRE: + if (!parsedtag_exists(tag, ATTR_FOR_TABLE)) + CLOSE_P; + if (!(obuf->flag & RB_IGNORE_P)) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + else + fillline(obuf, envs[h_env->envc].indent); + obuf->flag |= (RB_PRE | RB_IGNORE_P); + /* istr = str; */ + return 1; + case HTML_N_PRE: + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag &= ~RB_PRE; + close_anchor(h_env, obuf); + return 1; + case HTML_PRE_INT: + i = obuf->line->length; + append_tags(obuf); + if (!(obuf->flag & RB_SPECIAL)) { + set_breakpoint(obuf, obuf->line->length - i); + } + obuf->flag |= RB_PRE_INT; + return 0; + case HTML_N_PRE_INT: + push_tag(obuf, "</pre_int>", HTML_N_PRE_INT); + obuf->flag &= ~RB_PRE_INT; + if (!(obuf->flag & RB_SPECIAL) && obuf->pos > obuf->bp.pos) { + obuf->prevchar = '\0'; + obuf->prev_ctype = PC_CTRL; + } + return 1; + case HTML_NOBR: + obuf->flag |= RB_NOBR; + obuf->nobr_level++; + return 0; + case HTML_N_NOBR: + if (obuf->nobr_level > 0) + obuf->nobr_level--; + if (obuf->nobr_level == 0) + obuf->flag &= ~RB_NOBR; + return 0; + case HTML_LISTING: + CLOSE_P; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag |= (RB_LSTMODE | RB_IGNORE_P); + /* istr = str; */ + return 1; + case HTML_N_LISTING: + CLOSE_P; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag &= ~RB_LSTMODE; + return 1; + case HTML_XMP: + CLOSE_P; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag |= (RB_XMPMODE | RB_IGNORE_P); + /* istr = str; */ + return 1; + case HTML_N_XMP: + CLOSE_P; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag &= ~RB_XMPMODE; + return 1; + case HTML_SCRIPT: + obuf->flag |= RB_IGNORE; + obuf->ignore_tag = Strnew_charp("</script>"); + return 1; + case HTML_N_SCRIPT: + /* should not be reached */ + return 1; + case HTML_STYLE: + obuf->flag |= RB_IGNORE; + obuf->ignore_tag = Strnew_charp("</style>"); + return 1; + case HTML_N_STYLE: + /* should not be reached */ + return 1; + case HTML_PLAINTEXT: + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + obuf->flag |= RB_PLAIN; + /* istr = str; */ + return 1; + case HTML_A: + if (obuf->anchor) + close_anchor(h_env, obuf); + + hseq = 0; + + if (parsedtag_get_value(tag, ATTR_HREF, &p)) + obuf->anchor = Strnew_charp(p); + if (parsedtag_get_value(tag, ATTR_TARGET, &p)) + obuf->anchor_target = Strnew_charp(p); + if (parsedtag_get_value(tag, ATTR_HSEQ, &hseq)) + obuf->anchor_hseq = hseq; + + if (hseq == 0 && obuf->anchor) { + obuf->anchor_hseq = cur_hseq; + tmp = process_anchor(tag, h_env->tagbuf->ptr); + push_tag(obuf, tmp->ptr, HTML_A); + return 1; + } + return 0; + case HTML_N_A: + close_anchor(h_env, obuf); + return 1; + case HTML_IMG: + tmp = process_img(tag); + HTMLlineproc1(tmp->ptr, h_env); + return 1; + case HTML_IMG_ALT: + if (parsedtag_get_value(tag, ATTR_SRC, &p)) + obuf->img_alt = Strnew_charp(p); + return 0; + case HTML_N_IMG_ALT: + if (obuf->img_alt) { + if (!close_effect0(obuf, HTML_IMG_ALT)) + push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT); + obuf->img_alt = NULL; + } + return 1; + case HTML_TABLE: + obuf->table_level++; + if (obuf->table_level >= MAX_TABLE) + break; + w = BORDER_NONE; + /* x: cellspacing, y: cellpadding */ + x = 2; + y = 1; + z = 0; + width = 0; + if (parsedtag_exists(tag, ATTR_BORDER)) { + if (parsedtag_get_value(tag, ATTR_BORDER, &w)) { + if (w > 2) + w = BORDER_THICK; + else if (w < 0) { /* weird */ + w = BORDER_THIN; + } + } + else + w = BORDER_THIN; + } + if (parsedtag_get_value(tag, ATTR_WIDTH, &i)) { + if (obuf->table_level == 0) + width = REAL_WIDTH(i, h_env->limit - envs[h_env->envc].indent); + else + width = RELATIVE_WIDTH(i); + } + if (parsedtag_exists(tag, ATTR_HBORDER)) + w = BORDER_NOWIN; + parsedtag_get_value(tag, ATTR_CELLSPACING, &x); + parsedtag_get_value(tag, ATTR_CELLPADDING, &y); + parsedtag_get_value(tag, ATTR_VSPACE, &z); +#ifdef ID_EXT + parsedtag_get_value(tag, ATTR_ID, &id); +#endif /* ID_EXT */ + tables[obuf->table_level] = begin_table(w, x, y, z); +#ifdef ID_EXT + if (id != NULL) + tables[obuf->table_level]->id = Strnew_charp(id); +#endif /* ID_EXT */ + table_mode[obuf->table_level].pre_mode = 0; + table_mode[obuf->table_level].indent_level = 0; + table_mode[obuf->table_level].nobr_level = 0; + table_mode[obuf->table_level].caption = 0; +#ifndef TABLE_EXPAND + tables[obuf->table_level]->total_width = width; +#else + tables[obuf->table_level]->real_width = width; + tables[obuf->table_level]->total_width = 0; +#endif + return 1; + case HTML_N_TABLE: + /* should be processed in HTMLlineproc() */ + return 1; + case HTML_CENTER: + CLOSE_P; + if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P))) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + RB_SAVE_FLAG(obuf); + RB_SET_ALIGN(obuf, RB_CENTER); + return 1; + case HTML_N_CENTER: + CLOSE_P; + if (!(obuf->flag & RB_PREMODE)) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + RB_RESTORE_FLAG(obuf); + return 1; + case HTML_DIV: + CLOSE_P; + if (!(obuf->flag & RB_IGNORE_P)) + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + set_alignment(obuf, tag); + return 1; + case HTML_N_DIV: + CLOSE_P; + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + RB_RESTORE_FLAG(obuf); + return 1; + case HTML_FORM: +#ifdef NEW_FORM + case HTML_FORM_INT: + process_form(tag); + return 1; +#endif /* NEW_FORM */ + case HTML_N_FORM: +#ifdef NEW_FORM + case HTML_N_FORM_INT: + process_n_form(); + return 1; +#else /* not NEW_FORM */ + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + return 0; +#endif /* not NEW_FORM */ + case HTML_INPUT: + tmp = process_input(tag); + if (tmp) + HTMLlineproc1(tmp->ptr, h_env); + return 1; + case HTML_SELECT: + process_select(tag); + obuf->flag |= RB_INSELECT; + return 1; + case HTML_N_SELECT: + obuf->flag &= ~RB_INSELECT; + tmp = process_n_select(); + if (tmp) + HTMLlineproc1(tmp->ptr, h_env); + return 1; + case HTML_OPTION: + /* nothing */ + return 1; + case HTML_TEXTAREA: + process_textarea(tag, h_env->limit); + obuf->flag |= RB_INTXTA; + return 1; + case HTML_N_TEXTAREA: + close_textarea(h_env); + return 1; + case HTML_ISINDEX: + p = ""; + q = "!CURRENT_URL!"; + parsedtag_get_value(tag, ATTR_PROMPT, &p); + parsedtag_get_value(tag, ATTR_ACTION, &q); + tmp = Strnew_m_charp("<form method=get action=\"", + htmlquote_str(q), + "\">", + htmlquote_str(p), + "<input type=text name=\"\" accept></form>", + NULL); + HTMLlineproc1(tmp->ptr, h_env); + return 1; + case HTML_META: + p = q = NULL; + parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p); + parsedtag_get_value(tag, ATTR_CONTENT, &q); +#ifdef JP_CHARSET + if (p && q && !strcasecmp(p, "Content-Type") && + (q = strcasestr(q, "charset")) != NULL) { + q += 7; + SKIP_BLANKS(q); + if (*q == '=') { + q++; + SKIP_BLANKS(q); + content_charset = guess_charset(q); + } + } + else +#endif + if (p && q && !strcasecmp(p, "refresh")) { + int refresh = atoi(q); + Str s_tmp = NULL; + + while (*q) { + if (!strncasecmp(q, "url=", 4)) { + q += 4; + if (*q == '\"') /* " */ + q++; + r = q; + while (*r && !IS_SPACE(*r) && *r != ';') + r++; + s_tmp = Strnew_charp_n(q, r - q); + + if (s_tmp->ptr[s_tmp->length - 1] == '\"') { /* " + */ + s_tmp->length--; + s_tmp->ptr[s_tmp->length] = '\0'; + } + q = r; + } + while (*q && *q != ';') + q++; + if (*q == ';') + q++; + while (*q && *q == ' ') + q++; + } + if (s_tmp) { + q = htmlquote_str(s_tmp->ptr); + tmp = Sprintf("Refresh (%d sec) <a hseq=\"%d\" href=\"%s\">%s</a>", + refresh, cur_hseq++, q, q); + push_str(obuf, s_tmp->length, tmp, PC_ASCII); + flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit); + if (!is_redisplay && refresh == 0) { + pushEvent(FUNCNAME_goURL, s_tmp->ptr); + /* pushEvent(deletePrevBuf,NULL); */ + } + } + } + return 1; + case HTML_BASE: + case HTML_MAP: + case HTML_N_MAP: + case HTML_AREA: + return 0; + case HTML_DEL: + HTMLlineproc1("<U>[DEL:</U>", h_env); + return 1; + case HTML_N_DEL: + HTMLlineproc1("<U>:DEL]</U>", h_env); + return 1; + case HTML_INS: + HTMLlineproc1("<U>[INS:</U>", h_env); + return 1; + case HTML_N_INS: + HTMLlineproc1("<U>:INS]</U>", h_env); + return 1; + case HTML_FONT: + case HTML_N_FONT: + case HTML_NOP: + return 1; +#ifdef VIEW_UNSEENOBJECTS + case HTML_BGSOUND: + if (view_unseenobject) { + if (parsedtag_get_value(tag, ATTR_SRC, &p)) { + Str s; + q = htmlquote_str(p); + s = Sprintf("<A HREF=\"%s\">bgsound(%s)</A>", q, q); + HTMLlineproc1(s->ptr, h_env); + } + } + return 1; + case HTML_EMBED: + if (view_unseenobject) { + if (parsedtag_get_value(tag, ATTR_SRC, &p)) { + Str s; + q = htmlquote_str(p); + s = Sprintf("<A HREF=\"%s\">embed(%s)</A>", q, q); + HTMLlineproc1(s->ptr, h_env); + } + } + return 1; + case HTML_APPLET: + if (view_unseenobject) { + if (parsedtag_get_value(tag, ATTR_ARCHIVE, &p)) { + Str s; + q = htmlquote_str(p); + s = Sprintf("<A HREF=\"%s\">applet archive(%s)</A>", q, q); + HTMLlineproc1(s->ptr, h_env); + } + } + return 1; +#endif /* VIEW_UNSEENOBJECTS */ + case HTML_BODY: +#ifdef VIEW_UNSEENOBJECTS + if (view_unseenobject) { + if (parsedtag_get_value(tag, ATTR_BACKGROUND, &p)) { + Str s; + q = htmlquote_str(p); + s = Sprintf("<IMG SRC=\"%s\" ALT=\"bg image(%s)\"><BR>", + q, q); + HTMLlineproc1(s->ptr, h_env); + } + } +#endif /* VIEW_UNSEENOBJECTS */ + case HTML_N_BODY: + obuf->flag |= RB_IGNORE_P; + return 1; + default: +/* obuf->prevchar = '\0'; */ + return 0; + } + /* not reached */ + return 0; +} + +#define PPUSH(p,c) {outp[pos]=(p);outc[pos]=(c);pos++;} + +static TextLineListItem *_tl_lp2; + +static Str +textlist_feed() +{ + TextLine *p; + if (_tl_lp2 != NULL) { + p = _tl_lp2->ptr; + _tl_lp2 = _tl_lp2->next; + return p->line; + } + return NULL; +} + +static void +HTMLlineproc2body(Buffer * buf, Str (*feed) (), int llimit) +{ + Anchor *a_href = NULL, *a_img = NULL, *a_form = NULL; + char outc[LINELEN]; + char *p, *q, *r, *str; +#ifndef NEW_FORM + char cs; +#endif + Lineprop outp[LINELEN], mode, effect; + int pos; + int nlines; + FILE *debug; + struct frameset *frameset_s[FRAMESTACK_SIZE]; + int frameset_sp = -1; + union frameset_element *idFrame = NULL; + char *id = NULL; + Str tmp; + int hseq; + Str line; + char *endp; + + if (w3m_debug) + debug = fopen("zzzerr", "a"); + + effect = 0; + nlines = 0; +#ifdef NEW_FORM + buf->formlist = (form_max >= 0) ? forms[form_max] : NULL; +#endif /* NEW_FORM */ + while ((line = feed()) != NULL) { + if (w3m_debug) { + Strfputs(line, debug); + fputc('\n', debug); + } + proc_again: + if (++nlines == llimit) + break; + pos = 0; + if (showLineNum) { + tmp = Sprintf("%4d:", nlines); + for (p = tmp->ptr; *p; p++) { + PPUSH(PC_ASCII, *p); + } + } +#ifdef ENABLE_REMOVE_TRAILINGSPACES + Strremovetrailingspaces(line); +#endif + str = line->ptr; + endp = str + line->length; + while (str < endp && pos < LINELEN) { + mode = get_mctype(str); +#ifndef KANJI_SYMBOLS + if (effect & PC_RULE && IS_INTSPACE(*str)) { + PPUSH(PC_ASCII | effect, *str); + str++; + } else +#endif + if (mode == PC_CTRL || IS_INTSPACE(*str)) { + PPUSH(PC_ASCII | effect, ' '); + str++; + } +#ifdef JP_CHARSET + else if (mode == PC_KANJI) { + PPUSH(PC_KANJI1 | effect, str[0]); + PPUSH(PC_KANJI2 | effect, str[1]); + str += 2; + } +#endif + else if (mode == PC_ASCII && *str != '<' && *str != '&') { + PPUSH(mode | effect, *(str++)); + } + else if (*str == '&') { + /* + * & escape processing + */ + int emode; + p = getescapecmd(&str); + while (*p) { + emode = get_mctype(p); +#ifdef JP_CHARSET + if (emode == PC_KANJI) { + PPUSH(PC_KANJI1 | effect, p[0]); + PPUSH(PC_KANJI2 | effect, p[1]); + p += 2; + } + else +#endif + { + PPUSH(emode | effect, *(p++)); + } + } + } + else { + /* tag processing */ + struct parsed_tag *tag; + if (!(tag = parse_tag(&str, TRUE))) + continue; + switch (tag->tagid) { + case HTML_B: + effect |= PE_BOLD; + break; + case HTML_N_B: + effect &= ~PE_BOLD; + break; + case HTML_U: + effect |= PE_UNDER; + break; + case HTML_N_U: + effect &= ~PE_UNDER; + break; + case HTML_A: + if (renderFrameSet && + parsedtag_get_value(tag, ATTR_FRAMENAME, &p) && + (!idFrame || strcmp(idFrame->body->name, p))) { + idFrame = search_frame(renderFrameSet, p); + if (idFrame && idFrame->body->attr != F_BODY) + idFrame = NULL; + } + p = r = NULL; + q = buf->baseTarget; + hseq = 0; + id = NULL; + if (parsedtag_get_value(tag, ATTR_NAME, &id)) + registerName(buf, id, currentLn(buf), pos); + parsedtag_get_value(tag, ATTR_HREF, &p); + parsedtag_get_value(tag, ATTR_TARGET, &q); + parsedtag_get_value(tag, ATTR_REFERER, &r); + parsedtag_get_value(tag, ATTR_HSEQ, &hseq); + if (hseq > 0) + buf->hmarklist = + putHmarker(buf->hmarklist, currentLn(buf), + pos, hseq - 1); + if (id && idFrame) + idFrame->body->nameList = + putAnchor(idFrame->body->nameList, + id, + NULL, + (Anchor **) NULL, + NULL, + currentLn(buf), + pos); + if (p) { + effect |= PE_ANCHOR; + a_href = registerHref(buf, remove_space(p), q, + r, currentLn(buf), pos); + a_href->hseq = ((hseq > 0) ? hseq : -hseq) - 1; + } + break; + case HTML_N_A: + effect &= ~PE_ANCHOR; + if (a_href) { + a_href->end.line = currentLn(buf); + a_href->end.pos = pos; + if (a_href->start.line == a_href->end.line && + a_href->start.pos == a_href->end.pos) + a_href->hseq = -1; + a_href = NULL; + } + break; + case HTML_IMG_ALT: + if (parsedtag_get_value(tag, ATTR_SRC, &p)) { + a_img = registerImg(buf, p, + currentLn(buf), pos); + } + effect |= PE_IMAGE; + break; + case HTML_N_IMG_ALT: + effect &= ~PE_IMAGE; + if (a_img) { + a_img->end.line = currentLn(buf); + a_img->end.pos = pos; + } + a_img = NULL; + break; + case HTML_INPUT_ALT: + { + FormList *form; +#ifdef NEW_FORM + int form_id = -1; +#endif /* NEW_FORM */ + +#ifndef NEW_FORM + if (form_sp < 0) + break; /* outside of <form>..</form> */ +#endif /* not NEW_FORM */ + hseq = 0; + parsedtag_get_value(tag, ATTR_HSEQ, &hseq); +#ifdef NEW_FORM + parsedtag_get_value(tag, ATTR_FID, &form_id); +#endif +#ifdef NEW_FORM + if (form_id < 0 || form_id > form_max || forms == NULL) + break; /* outside of <form>..</form> */ + form = forms[form_id]; +#else /* not NEW_FORM */ + if (form_sp >= FORMSTACK_SIZE) + break; + form = form_stack[form_sp]; +#endif /* not NEW_FORM */ + if (hseq > 0) { + int hpos = pos; + if (*str == '[') + hpos++; + buf->hmarklist = + putHmarker(buf->hmarklist, currentLn(buf), hpos, hseq - 1); + } + if (!form->target) + form->target = buf->baseTarget; + a_form = registerForm(buf, form, tag, currentLn(buf), pos); + if (a_form) { + a_form->hseq = hseq - 1; + if (!parsedtag_exists(tag, ATTR_NO_EFFECT)) + effect |= PE_FORM; + break; + } + } + case HTML_N_INPUT_ALT: + effect &= ~PE_FORM; + if (a_form) { + a_form->end.line = currentLn(buf); + a_form->end.pos = pos; + if (a_form->start.line == a_form->end.line && + a_form->start.pos == a_form->end.pos) + a_form->hseq = -1; + } + a_form = NULL; + break; +#ifndef NEW_FORM + case HTML_FORM: + case HTML_FORM_INT: + form_sp++; + if (form_sp >= FORMSTACK_SIZE) + break; + p = "get"; + q = "/"; + s = NULL; + cs = 0; + parsedtag_get_value(tag, ATTR_METHOD, &p); + parsedtag_get_value(tag, ATTR_ACTION, &q); +#ifdef JP_CHARSET + if (parsedtag_get_value(tag, ATTR_CHARSET, &r)) + cs = check_charset(r); +#endif + parsedtag_get_value(tag, ATTR_ENCTYPE, &s); + buf->formlist = newFormList(q, p, &cs, s, buf->formlist); + form_stack[form_sp] = buf->formlist; + break; + case HTML_N_FORM: + case HTML_N_FORM_INT: + if (form_sp >= 0) + form_sp--; + break; +#endif /* not NEW_FORM */ + case HTML_MAP: + if (parsedtag_get_value(tag, ATTR_NAME, &p)) { + MapList *m = New(MapList); + m->name = Strnew_charp(p); + m->next = buf->maplist; + m->urls = newTextList(); + m->alts = newTextList(); + buf->maplist = m; + } + break; + case HTML_N_MAP: + /* nothing to do */ + break; + case HTML_AREA: + if (buf->maplist == NULL) /* outside of * + * <map>..</map> */ + break; + if (parsedtag_get_value(tag, ATTR_HREF, &p)) { + pushText(buf->maplist->urls, p); + if (parsedtag_get_value(tag, ATTR_ALT, &q)) + pushText(buf->maplist->alts, q); + else + pushText(buf->maplist->alts, ""); + } + break; + case HTML_FRAMESET: + frameset_sp++; + if (frameset_sp >= FRAMESTACK_SIZE) + break; + frameset_s[frameset_sp] = newFrameSet(tag); + if (frameset_s[frameset_sp] == NULL) + break; + if (frameset_sp == 0) { + if (buf->frameset == NULL) { + buf->frameset = frameset_s[frameset_sp]; + } + else + pushFrameTree(&(buf->frameQ), frameset_s[frameset_sp], 0, 0); + } + else + addFrameSetElement(frameset_s[frameset_sp - 1], + *(union frameset_element *) &frameset_s[frameset_sp]); + break; + case HTML_N_FRAMESET: + if (frameset_sp >= 0) + frameset_sp--; + break; + case HTML_FRAME: + if (frameset_sp >= 0 && frameset_sp < FRAMESTACK_SIZE) { + union frameset_element element; + + element.body = newFrame(tag, baseURL(buf)); + addFrameSetElement(frameset_s[frameset_sp], element); + } + break; + case HTML_BASE: + if (parsedtag_get_value(tag, ATTR_HREF, &p)) { + if (!buf->baseURL) + buf->baseURL = New(ParsedURL); + parseURL(p, buf->baseURL, NULL); + } + parsedtag_get_value(tag, ATTR_TARGET, &buf->baseTarget); + break; + case HTML_TITLE_ALT: + if (parsedtag_get_value(tag, ATTR_TITLE, &p)) + buf->buffername = cleanup_str(p); + break; +#ifndef KANJI_SYMBOLS + case HTML_RULE: + effect |= PC_RULE; + break; + case HTML_N_RULE: + effect &= ~PC_RULE; + break; +#endif /* not KANJI_SYMBOLS */ + } +#ifdef ID_EXT + id = NULL; + if (parsedtag_get_value(tag, ATTR_ID, &id)) + registerName(buf, id, currentLn(buf), pos); + if (renderFrameSet && + parsedtag_get_value(tag, ATTR_FRAMENAME, &p) && + (!idFrame || strcmp(idFrame->body->name, p))) { + idFrame = search_frame(renderFrameSet, p); + if (idFrame && idFrame->body->attr != F_BODY) + idFrame = NULL; + } + if (id && idFrame) + idFrame->body->nameList = + putAnchor(idFrame->body->nameList, + id, + NULL, + (Anchor **) NULL, + NULL, + currentLn(buf), pos); +#endif /* ID_EXT */ + } + } + /* end of processing for one line */ + addnewline(buf, outc, outp, +#ifdef ANSI_COLOR + NULL, +#endif + pos, nlines); + if (str != endp) { + line = Strsubstr(line, str - line->ptr, endp - str); + goto proc_again; + } + } + if (w3m_debug) + fclose(debug); +} + +void +HTMLlineproc2(Buffer * buf, TextLineList * tl) +{ + _tl_lp2 = tl->first; + HTMLlineproc2body(buf, textlist_feed, -1); +} + +static InputStream _file_lp2; + +static Str +file_feed() +{ + Str s; + s = StrISgets(_file_lp2); + if (s->length == 0) { + ISclose(_file_lp2); + return NULL; + } + return s; +} + +void +HTMLlineproc3(Buffer * buf, InputStream stream) +{ + _file_lp2 = stream; + HTMLlineproc2body(buf, file_feed, -1); +} + +static void +proc_escape(struct readbuffer *obuf, char **str_return) +{ + char *str = *str_return, *estr; + int ech = getescapechar(str_return); + int width, n_add = *str_return - str; + Lineprop mode = IS_CNTRL(ech) ? PC_CTRL : PC_ASCII; + + if (!ech) { + *str_return = str; + proc_mchar(obuf, obuf->flag & RB_SPECIAL, 1, str_return, PC_ASCII); + return; + } + + check_breakpoint(obuf, obuf->flag & RB_SPECIAL, ech); + estr = conv_latin1(ech); + width = strlen(estr); + if (width == 1 && ech == (unsigned char) *estr && + ech != '&' && ech != '<' && ech != '>') + push_charp(obuf, width, estr, mode); + else + push_nchars(obuf, width, str, n_add, mode); + obuf->prevchar = ech; + obuf->prev_ctype = mode; +} + + +static int +need_flushline(struct html_feed_environ *h_env, struct readbuffer *obuf, + Lineprop mode) +{ + char ch = Strlastchar(obuf->line); + + if (obuf->flag & RB_PRE_INT) { + if (obuf->pos > h_env->limit) + return 1; + else + return 0; + } + + /* if (ch == ' ' && obuf->tag_sp > 0) */ + if (ch == ' ') + return 0; + + if (obuf->pos > h_env->limit) + return 1; + + return 0; +} + +static int +table_width(struct html_feed_environ *h_env, int table_level) +{ + int width; + if (table_level < 0) + return 0; + width = tables[table_level]->total_width; + if (table_level > 0 || width > 0) + return width; + return h_env->limit - h_env->envs[h_env->envc].indent; +} + +/* HTML processing first pass */ +void +HTMLlineproc0(char *istr, struct html_feed_environ *h_env, int internal) +{ + Lineprop mode; + char *str = istr, *q; + int cmd; + struct readbuffer *obuf = h_env->obuf; + int indent, delta; + struct parsed_tag *tag; + Str tokbuf = Strnew(); + struct table *tbl = NULL; + struct table_mode *tbl_mode; + int tbl_width; + + if (w3m_debug) { + FILE *f = fopen("zzzproc1", "a"); + fprintf(f, "%c%c%c%c", + (obuf->flag & RB_PREMODE) ? 'P' : ' ', + (obuf->table_level >= 0) ? 'T' : ' ', + (obuf->flag & RB_INTXTA) ? 'X' : ' ', + (obuf->flag & RB_IGNORE) ? 'I' : ' '); + fprintf(f, "HTMLlineproc1(\"%s\",%d,%lx)\n", istr, h_env->limit, (unsigned long) h_env); + fclose(f); + } + + /* comment processing */ + if (obuf->status == R_ST_CMNT || obuf->status == R_ST_NCMNT3 || + obuf->status == R_ST_IRRTAG) { + while (*str != '\0' && obuf->status != R_ST_NORMAL) { + next_status(*str, &obuf->status); + str++; + } + if (obuf->status != R_ST_NORMAL) + return; + } + + table_start: + if (obuf->table_level >= 0) { + int level = min(obuf->table_level, MAX_TABLE - 1); + tbl = tables[level]; + tbl_mode = &table_mode[level]; + tbl_width = table_width(h_env, level); + } + + while (*str != '\0') { + int is_tag = FALSE; + + if (obuf->flag & RB_PLAIN) + goto read_as_plain; /* don't process tag */ + + if (*str == '<' || ST_IS_TAG(obuf->status)) { + int pre_mode = (obuf->table_level >= 0) ? + tbl_mode->pre_mode & TBLM_PLAIN : + obuf->flag & RB_PLAINMODE; + /* + * Tag processing + */ + if (ST_IS_TAG(obuf->status)) { +/*** continuation of a tag ***/ + read_token(h_env->tagbuf, &str, &obuf->status, + pre_mode, 1); + } + else { + if (!REALLY_THE_BEGINNING_OF_A_TAG(str)) { + /* this is NOT a beginning of a tag */ + obuf->status = R_ST_NORMAL; + HTMLlineproc1("<", h_env); + str++; + continue; + } + read_token(h_env->tagbuf, &str, &obuf->status, + pre_mode, 0); + } + if (ST_IS_COMMENT(obuf->status)) { + if (obuf->flag & RB_IGNORE) + /* within ignored tag, such as * + * <script>..</script>, don't process comment. */ + obuf->status = R_ST_NORMAL; + return; + } + if (h_env->tagbuf->length == 0) + continue; + if (obuf->status != R_ST_NORMAL) { + if (!pre_mode) { + if (Strlastchar(h_env->tagbuf) == '\n') + Strchop(h_env->tagbuf); + if (ST_IS_REAL_TAG(obuf->status)) + Strcat_char(h_env->tagbuf, ' '); + } + continue; + } + is_tag = TRUE; + q = h_env->tagbuf->ptr; + } + + if (obuf->flag & (RB_INTXTA + | RB_INSELECT + | RB_IGNORE)) { + cmd = HTML_UNKNOWN; + if (!is_tag) { + read_token(tokbuf, &str, &obuf->status, + (obuf->flag & RB_INTXTA) ? 1 : 0, 0); + if (obuf->status != R_ST_NORMAL) + continue; + q = tokbuf->ptr; + } + else { + char *p = q; + cmd = gethtmlcmd(&p, NULL); + } + + /* textarea */ + if (obuf->flag & RB_INTXTA) { + if (cmd == HTML_N_TEXTAREA) + goto proc_normal; + feed_textarea(q); + } + else if (obuf->flag & RB_INSELECT) { + if (cmd == HTML_N_SELECT || cmd == HTML_N_FORM) + goto proc_normal; + feed_select(q); + } + /* script */ + else if (obuf->flag & RB_IGNORE) { + if (TAG_IS(q, obuf->ignore_tag->ptr, + obuf->ignore_tag->length - 1)) { + obuf->flag &= ~RB_IGNORE; + } + } + continue; + } + + if (obuf->table_level >= 0) { + /* + * within table: in <table>..</table>, all input tokens + * are fed to the table renderer, and then the renderer + * makes HTML output. + */ + + if (!is_tag) { + read_token(tokbuf, &str, &obuf->status, + tbl_mode->pre_mode & TBLM_PREMODE, 0); + if (obuf->status != R_ST_NORMAL) + continue; + q = tokbuf->ptr; + } + + switch (feed_table(tbl, q, tbl_mode, tbl_width, internal)) { + case 0: + /* </table> tag */ + obuf->table_level--; + if (obuf->table_level >= MAX_TABLE - 1) + continue; + end_table(tbl); + if (obuf->table_level >= 0) { + Str tmp; + struct table *tbl0 = tables[obuf->table_level]; + tmp = Sprintf("<table_alt tid=%d>", tbl0->ntable); + pushTable(tbl0, tbl); + tbl = tbl0; + tbl_mode = &table_mode[obuf->table_level]; + tbl_width = table_width(h_env, obuf->table_level); + feed_table(tbl, tmp->ptr, tbl_mode, tbl_width, TRUE); + continue; + /* continue to the next */ + } + /* all tables have been read */ + if (tbl->vspace > 0 && !(obuf->flag & RB_IGNORE_P)) { + int indent = h_env->envs[h_env->envc].indent; + flushline(h_env, obuf, indent, 0, h_env->limit); + do_blankline(h_env, obuf, indent, 0, h_env->limit); + } + save_fonteffect(h_env, obuf); + renderTable(tbl, tbl_width, h_env); + restore_fonteffect(h_env, obuf); + obuf->flag &= ~RB_IGNORE_P; + if (tbl->vspace > 0) { + int indent = h_env->envs[h_env->envc].indent; + do_blankline(h_env, obuf, indent, 0, h_env->limit); + obuf->flag |= RB_IGNORE_P; + } + obuf->prevchar = ' '; + continue; + case 1: + /* <table> tag */ + goto proc_normal; + default: + continue; + } + } + + proc_normal: + if (is_tag) { +/*** Beginning of a new tag ***/ + if ((tag = parse_tag(&q, internal))) + cmd = tag->tagid; + else + cmd = HTML_UNKNOWN; + if (((obuf->flag & RB_XMPMODE) && cmd != HTML_N_XMP) || + ((obuf->flag & RB_LSTMODE) && cmd != HTML_N_LISTING)) { + Str tmp = Strdup(h_env->tagbuf); + Strcat_charp(tmp, str); + str = tmp->ptr; + goto read_as_plain; + } + if (cmd == HTML_UNKNOWN) + continue; + /* process tags */ + if (HTMLtagproc1(tag, h_env) == 0) + { + /* preserve the tag for second-stage processing */ + if (parsedtag_need_reconstruct(tag)) + h_env->tagbuf = parsedtag2str(tag); + push_tag(obuf, h_env->tagbuf->ptr, cmd); + } +#ifdef ID_EXT + else { + process_idattr(obuf, cmd, tag); + } +#endif /* ID_EXT */ + obuf->bp.init_flag = 1; + clear_ignore_p_flag(cmd, obuf); + if (cmd == HTML_TABLE) + goto table_start; + else + continue; + } + + read_as_plain: + mode = get_mctype(str); + delta = get_mclen(mode); + if (obuf->flag & (RB_SPECIAL & ~RB_NOBR)) { + if (*str != '\n') + obuf->flag &= ~RB_IGNORE_P; + if (*str == '\n') { + str++; + if (obuf->flag & RB_IGNORE_P) { + obuf->flag &= ~RB_IGNORE_P; + continue; + } + if (obuf->flag & RB_PRE_INT) + PUSH(' '); + else + flushline(h_env, obuf, h_env->envs[h_env->envc].indent, 1, h_env->limit); + } + else if (*str == '\t') { + do { + PUSH(' '); + } while (obuf->pos % Tabstop != 0); + str++; + } + else if (obuf->flag & RB_PLAINMODE) { + char *p = htmlquote_char(*str); + if (p) { + push_charp(obuf, 1, p, PC_ASCII); + str++; + } + else { + proc_mchar(obuf, 1, delta, &str, mode); + } + } + else { + if (*str == '&') + proc_escape(obuf, &str); + else + proc_mchar(obuf, 1, delta, &str, mode); + } + if (obuf->flag & (RB_SPECIAL & ~RB_PRE_INT)) + continue; + } + else { + if (!IS_SPACE(*str)) + obuf->flag &= ~RB_IGNORE_P; + if ((mode == PC_ASCII || mode == PC_CTRL) && IS_SPACE(*str)) { + if (obuf->prevchar != ' ') { + PUSH(' '); + } + str++; + } + else { +#ifdef JP_CHARSET + if (mode == PC_KANJI && + obuf->pos > h_env->envs[h_env->envc].indent && + Strlastchar(obuf->line) == ' ') { + while (obuf->line->length >= 2 && + !strncmp(obuf->line->ptr + obuf->line->length - 2, " ", 2) && + obuf->pos >= h_env->envs[h_env->envc].indent) { + Strshrink(obuf->line, 1); + obuf->pos--; + } + if (obuf->line->length >= 3 && + obuf->prev_ctype == PC_KANJI && + Strlastchar(obuf->line) == ' ' && + obuf->pos >= h_env->envs[h_env->envc].indent) { + Strshrink(obuf->line, 1); + obuf->pos--; + } + } +#endif /* JP_CHARSET */ + if (*str == '&') + proc_escape(obuf, &str); + else + proc_mchar(obuf, obuf->flag & RB_SPECIAL, delta, &str, mode); + } + } + if (need_flushline(h_env, obuf, mode)) { + char *bp = obuf->line->ptr + obuf->bp.len; + char *tp = bp - obuf->bp.tlen; + int i = 0; + + if (tp > obuf->line->ptr && tp[-1] == ' ') + i = 1; + + indent = h_env->envs[h_env->envc].indent; + if (obuf->bp.pos - i > indent) { + Str line; + append_tags(obuf); + line = Strnew_charp(bp); + Strshrink(obuf->line, obuf->line->length - obuf->bp.len); +#ifdef FORMAT_NICE + if (obuf->pos - i > h_env->limit) + obuf->flag |= RB_FILL; +#endif /* FORMAT_NICE */ + back_to_breakpoint(obuf); + flushline(h_env, obuf, indent, 0, h_env->limit); +#ifdef FORMAT_NICE + obuf->flag &= ~RB_FILL; +#endif /* FORMAT_NICE */ + HTMLlineproc1(line->ptr, h_env); + } + } + } + if (!(obuf->flag & (RB_PREMODE | RB_NOBR | RB_INTXTA | RB_INSELECT + | RB_PLAINMODE | RB_IGNORE))) { + char *tp; + int i = 0; + + if (obuf->bp.pos == obuf->pos) { + tp = &obuf->line->ptr[obuf->bp.len - obuf->bp.tlen]; + } + else { + tp = &obuf->line->ptr[obuf->line->length]; + } + + if (tp > obuf->line->ptr && tp[-1] == ' ') + i = 1; + indent = h_env->envs[h_env->envc].indent; + if (obuf->pos - i > h_env->limit) { +#ifdef FORMAT_NICE + obuf->flag |= RB_FILL; +#endif /* FORMAT_NICE */ + flushline(h_env, obuf, indent, 0, h_env->limit); +#ifdef FORMAT_NICE + obuf->flag &= ~RB_FILL; +#endif /* FORMAT_NICE */ + } + } +} + +static void +close_textarea(struct html_feed_environ *h_env) +{ + Str tmp; + + h_env->obuf->flag &= ~RB_INTXTA; + tmp = process_n_textarea(); + if (tmp != NULL) + HTMLlineproc1(tmp->ptr, h_env); +} + +extern char *NullLine; +extern Lineprop NullProp[]; + +static void +addnewline(Buffer * buf, char *line, Lineprop * prop, +#ifdef ANSI_COLOR + Linecolor * color, +#endif + int pos, int nlines) +{ + Line *l; + l = New(Line); + l->next = NULL; + if (pos > 0) { + l->lineBuf = allocStr(line, pos); + l->propBuf = New_N(Lineprop, pos); + bcopy((void *) prop, (void *) l->propBuf, pos * sizeof(Lineprop)); + } + else { + l->lineBuf = NullLine; + l->propBuf = NullProp; + } +#ifdef ANSI_COLOR + if (pos > 0 && color) { + l->colorBuf = New_N(Linecolor, pos); + bcopy((void *) color, (void *) l->colorBuf, pos * sizeof(Linecolor)); + } else { + l->colorBuf = NULL; + } +#endif + l->len = pos; + l->width = -1; + l->prev = buf->currentLine; + if (buf->currentLine) { + l->next = buf->currentLine->next; + buf->currentLine->next = l; + } + else + l->next = NULL; + if (buf->lastLine == NULL || buf->lastLine == buf->currentLine) + buf->lastLine = l; + buf->currentLine = l; + if (buf->firstLine == NULL) + buf->firstLine = l; + l->linenumber = ++buf->allLine; + if (nlines < 0) { + l->real_linenumber = l->linenumber; + } + else { + l->real_linenumber = nlines; + } + l = NULL; +} + +/* + * loadHTMLBuffer: read file and make new buffer + */ +Buffer * +loadHTMLBuffer(URLFile * f, Buffer * newBuf) +{ + FILE *src = NULL; + Str tmp; + + if (newBuf == NULL) + newBuf = newBuffer(INIT_BUFFER_WIDTH); + if (newBuf->sourcefile == NULL && f->scheme != SCM_LOCAL) { + tmp = tmpfname(TMPF_SRC, ".html"); + src = fopen(tmp->ptr, "w"); + if (src) + newBuf->sourcefile = tmp->ptr; + } + + loadHTMLstream(f, newBuf, src, newBuf->bufferprop & BP_FRAME); + + newBuf->topLine = newBuf->firstLine; + newBuf->lastLine = newBuf->currentLine; + newBuf->currentLine = newBuf->firstLine; + if (src) + fclose(src); + + return newBuf; +} + +static char *_size_unit[] = +{"b", "kb", "Mb", "Gb", "Tb", + "Pb", "Eb", "Zb", "Bb", "Yb", NULL}; + +char * +convert_size(int size, int usefloat) +{ + float csize; + int sizepos = 0; + char **sizes = _size_unit; + + csize = (float) size; + while (csize >= 999.495 && sizes[sizepos + 1]) { + csize = csize / 1024.0; + sizepos++; + } + return Sprintf(usefloat ? "%.3g%s" : "%.0f%s", + floor(csize * 100.0 + 0.5) / 100.0, + sizes[sizepos])->ptr; +} + +char * +convert_size2(int size1, int size2, int usefloat) +{ + char **sizes = _size_unit; + float csize, factor = 1; + int sizepos = 0; + + csize = (float)((size1 > size2) ? size1 : size2); + while (csize / factor >= 999.495 && sizes[sizepos + 1]) { + factor *= 1024.0; + sizepos++; + } + return Sprintf(usefloat ? "%.3g/%.3g%s" : "%.0f/%.0f%s", + floor(size1 / factor * 100.0 + 0.5) / 100.0, + floor(size2 / factor * 100.0 + 0.5) / 100.0, + sizes[sizepos])->ptr; +} + +void +showProgress(int *linelen, int *trbyte) +{ + int i, j, rate, duration, eta, pos; + static time_t last_time, start_time; + time_t cur_time = time(0); + Str messages; + char *fmtrbyte, *fmrate; + + if (!fmInitialized) + return; + + if (current_content_length > 0) { + double ratio; + if (cur_time == last_time) + return; + last_time = cur_time; + if (*trbyte == 0) { + move(LASTLINE, 0); + clrtoeolx(); + start_time = cur_time; + } + *trbyte += *linelen; + *linelen = 0; + move(LASTLINE, 0); + ratio = 100.0 * (*trbyte) / current_content_length; + fmtrbyte = convert_size2(*trbyte, current_content_length, 1); + duration = cur_time - start_time; + if (duration) { + rate = *trbyte / duration; + fmrate = convert_size(rate, 1); + eta = rate ? (current_content_length - *trbyte) / rate : -1; + messages = Sprintf("%11s %3.0f%% " + "%7s/s " + "eta %02d:%02d:%02d ", + fmtrbyte, ratio, + fmrate, + eta / (60 * 60), (eta / 60) % 60, eta % 60); + } + else { + messages = Sprintf("%11s %3.0f%% ", + fmtrbyte, ratio); + } + addstr(messages->ptr); + pos = 42; + i = pos + (COLS - pos - 1) * (*trbyte) / current_content_length; + move(LASTLINE, pos); +#if 0 /* def KANJI_SYMBOLS */ + for (j = pos; j <= i; j += 2) + addstr("��"); +#else /* not 0 */ + standout(); + addch(' '); + for (j = pos + 1; j <= i; j++) + addch('|'); + standend(); +#endif /* not 0 */ + /* no_clrtoeol(); */ + refresh(); + } + else if (*linelen > 1000) { + if (cur_time == last_time) + return; + last_time = cur_time; + if (*trbyte == 0) { + move(LASTLINE, 0); + clrtoeolx(); + start_time = cur_time; + } + *trbyte += *linelen; + *linelen = 0; + move(LASTLINE, 0); + fmtrbyte = convert_size(*trbyte, 1); + duration = cur_time - start_time; + if (duration) { + fmrate = convert_size(*trbyte / duration, 1); + messages = Sprintf("%7s loaded %7s/s\n", fmtrbyte, fmrate); + } + else { + messages = Sprintf("%7s loaded\n", fmtrbyte); + } + message(messages->ptr, 0, 0); + refresh(); + } +} + +void +init_henv(struct html_feed_environ *h_env, struct readbuffer *obuf, + struct environment *envs, int nenv, TextLineList * buf, + int limit, int indent) +{ + envs[0].indent = indent; + + obuf->line = Strnew(); + obuf->cprop = 0; + obuf->pos = 0; + obuf->prevchar = ' '; + obuf->flag = RB_IGNORE_P; + obuf->flag_sp = 0; + obuf->status = R_ST_NORMAL; + obuf->table_level = -1; + obuf->nobr_level = 0; + obuf->anchor = 0; + obuf->anchor_target = 0; + obuf->anchor_hseq = 0; + obuf->img_alt = 0; + obuf->in_bold = 0; + obuf->in_under = 0; + obuf->prev_ctype = PC_ASCII; + obuf->tag_sp = 0; + obuf->fontstat_sp = 0; + obuf->bp.init_flag = 1; + set_breakpoint(obuf, 0); + + h_env->buf = buf; + h_env->f = NULL; + h_env->obuf = obuf; + h_env->tagbuf = Strnew(); + h_env->limit = limit; + h_env->maxlimit = 0; + h_env->envs = envs; + h_env->nenv = nenv; + h_env->envc = 0; + h_env->envc_real = 0; + h_env->title = NULL; + h_env->blank_lines = 0; +} + +void +completeHTMLstream(struct html_feed_environ *h_env, struct readbuffer *obuf) +{ + close_anchor(h_env, obuf); + if (obuf->img_alt) { + push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT); + obuf->img_alt = NULL; + } + if (obuf->in_bold) { + push_tag(obuf, "</b>", HTML_N_B); + obuf->in_bold = 0; + } + if (obuf->in_under) { + push_tag(obuf, "</u>", HTML_N_U); + obuf->in_under = 0; + } + /* for unbalanced select tag */ + if (obuf->flag & RB_INSELECT) + HTMLlineproc1("</select>", h_env); + + /* for unbalanced table tag */ + while (obuf->table_level >= 0) { + table_mode[obuf->table_level].pre_mode + &= ~(TBLM_IGNORE | TBLM_XMP | TBLM_LST); + HTMLlineproc1("</table>", h_env); + } +} + +void +loadHTMLstream(URLFile * f, Buffer * newBuf, FILE * src, int internal) +{ + struct environment envs[MAX_ENV_LEVEL]; + int linelen = 0; + int trbyte = 0; + Str lineBuf2 = Strnew(); + char code; + struct html_feed_environ htmlenv1; + struct readbuffer obuf; + MySignalHandler(*prevtrap) (); + + if (SETJMP(AbortLoading) != 0) { + HTMLlineproc1("<br>Transfer Interrupted!<br>", &htmlenv1); + goto phase2; + } + if (fmInitialized) { + prevtrap = signal(SIGINT, KeyAbort); + term_cbreak(); + } + + n_textarea = 0; + cur_textarea = NULL; +#ifdef MENU_SELECT + n_select = 0; +#endif /* MENU_SELECT */ + cur_select = NULL; + form_sp = -1; +#ifdef NEW_FORM + form_max = -1; + forms_size = 0; + forms = NULL; +#endif /* NEW_FORM */ + + cur_hseq = 1; + + if (w3m_halfload) { + newBuf->buffername = "---"; +#ifdef JP_CHARSET + newBuf->document_code = InnerCode; +#endif /* JP_CHARSET */ + HTMLlineproc3(newBuf, f->stream); + w3m_halfload = FALSE; + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + return; + } + + init_henv(&htmlenv1, &obuf, envs, MAX_ENV_LEVEL, NULL, newBuf->width, 0); + + if (w3m_halfdump) + htmlenv1.f = stdout; + else + htmlenv1.buf = newTextLineList(); + +#ifdef JP_CHARSET + if (newBuf != NULL && newBuf->document_code != '\0') + code = newBuf->document_code; + else if (content_charset != '\0') + code = content_charset; + else + code = DocumentCode; + content_charset = '\0'; +#endif +#if 0 + do_blankline(&htmlenv1, &obuf, 0, 0, htmlenv1.limit); + obuf.flag = RB_IGNORE_P; +#endif + if (IStype(f->stream) != IST_ENCODED) + f->stream = newEncodedStream(f->stream, f->encoding); + while ((lineBuf2 = StrmyUFgets(f))->length) { + if (src) + Strfputs(lineBuf2, src); + linelen += lineBuf2->length; + showProgress(&linelen, &trbyte); +#ifdef JP_CHARSET + if (content_charset != '\0') { /* <META> */ + code = content_charset; + content_charset = '\0'; + } +#endif + if (!internal) + lineBuf2 = convertLine(f, lineBuf2, &code, HTML_MODE); +#ifdef USE_NNTP + if (f->scheme == SCM_NEWS) { + if (Str_news_endline(lineBuf2)) { + iseos(f->stream) = TRUE; + break; + } + } +#endif /* USE_NNTP */ + HTMLlineproc0(lineBuf2->ptr, &htmlenv1, internal); + } + if (obuf.status != R_ST_NORMAL) + HTMLlineproc1(correct_irrtag(obuf.status)->ptr, &htmlenv1); + obuf.status = R_ST_NORMAL; + completeHTMLstream(&htmlenv1, &obuf); + flushline(&htmlenv1, &obuf, 0, 2, htmlenv1.limit); + if (htmlenv1.title) + newBuf->buffername = htmlenv1.title; + if (w3m_halfdump) { + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + return; + } + phase2: + newBuf->trbyte = trbyte + linelen; + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + HTMLlineproc2(newBuf, htmlenv1.buf); +#ifdef JP_CHARSET + newBuf->document_code = code; +#endif /* JP_CHARSET */ +} + +/* + * loadHTMLString: read string and make new buffer + */ +Buffer * +loadHTMLString(Str page) +{ + URLFile f; + MySignalHandler(*prevtrap) (); + Buffer *newBuf; + + newBuf = newBuffer(INIT_BUFFER_WIDTH); + if (SETJMP(AbortLoading) != 0) { + discardBuffer(newBuf); + return NULL; + } + init_stream(&f, SCM_LOCAL, newStrStream(page)); + + if (fmInitialized) { + prevtrap = signal(SIGINT, KeyAbort); + term_cbreak(); + } + + loadHTMLstream(&f, newBuf, NULL, TRUE); + + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + newBuf->topLine = newBuf->firstLine; + newBuf->lastLine = newBuf->currentLine; + newBuf->currentLine = newBuf->firstLine; +#ifdef JP_CHARSET + newBuf->document_code = InnerCode; +#endif /* JP_CHARSET */ + + return newBuf; +} + +#ifdef USE_GOPHER + +/* + * loadGopherDir: get gopher directory + */ +Buffer * +loadGopherDir(URLFile * uf, Buffer * newBuf) +{ +#ifdef JP_CHARSET + char code, ic; +#endif + Str name, file, host, port; + char type; + char *p; + TextLineList *tl = newTextLineList(); + Str lbuf; + int hseq = 1; + + if (newBuf == NULL) + newBuf = newBuffer(INIT_BUFFER_WIDTH); +#ifdef JP_CHARSET + if (newBuf->document_code != '\0') + code = newBuf->document_code; + else if (content_charset != '\0') + code = content_charset; + else + code = DocumentCode; + content_charset = '\0'; +#endif + while (1) { + if (lbuf = StrUFgets(uf), lbuf->length == 0) + break; + if (lbuf->ptr[0] == '.' && + (lbuf->ptr[1] == '\n' || lbuf->ptr[1] == '\r')) + break; +#ifdef JP_CHARSET + if ((ic = checkShiftCode(lbuf, code)) != '\0') + lbuf = conv_str(lbuf, (code = ic), InnerCode); +#endif /* JP_CHARSET */ + cleanup_line(lbuf, HTML_MODE); + + p = lbuf->ptr; + for (name = Strnew(); *p && *p != '\t'; p++) + Strcat_char(name, *p); + p++; + for (file = Strnew(); *p && *p != '\t'; p++) + Strcat_char(file, *p); + p++; + for (host = Strnew(); *p && *p != '\t'; p++) + Strcat_char(host, *p); + p++; + for (port = Strnew(); *p && + *p != '\t' && *p != '\r' && *p != '\n'; + p++) + Strcat_char(port, *p); + p++; + type = name->ptr[0]; + switch (type) { + case '0': + p = "[text file] "; + break; + case '1': + p = "[directory] "; + break; + case 'm': + p = "[message] "; + break; + case 's': + p = "[sound] "; + break; + case 'g': + p = "[gif] "; + break; + case 'h': + p = "[HTML] "; + break; + default: + p = "[unsupported]"; + break; + } + lbuf = Sprintf("<A HSEQ=\"%d\" HREF=\"gopher://", hseq++); + Strcat(lbuf, host); + Strcat_char(lbuf, ':'); + Strcat(lbuf, port); + Strcat_char(lbuf, '/'); + Strcat(lbuf, file); + Strcat_charp(lbuf, "\">"); + Strcat_charp(lbuf, p); + Strcat_charp(lbuf, name->ptr + 1); + pushTextLine(tl, newTextLine(lbuf, visible_length(lbuf->ptr))); + } + HTMLlineproc2(newBuf, tl); + newBuf->topLine = newBuf->firstLine; + newBuf->lastLine = newBuf->currentLine; + newBuf->currentLine = newBuf->firstLine; + + return newBuf; +} +#endif /* USE_GOPHER */ + +/* + * loadBuffer: read file and make new buffer + */ +Buffer * +loadBuffer(URLFile * uf, Buffer * newBuf) +{ + FILE *src = NULL; + char code; + Str lineBuf2; + char pre_lbuf = '\0'; + int nlines; + Str tmpf; + int linelen = 0, trbyte = 0; +#ifdef ANSI_COLOR + int check_color; +#endif + MySignalHandler(*prevtrap) (); + + if (newBuf == NULL) + newBuf = newBuffer(INIT_BUFFER_WIDTH); + lineBuf2 = Strnew(); + + if (SETJMP(AbortLoading) != 0) { + goto _end; + } + if (fmInitialized) { + prevtrap = signal(SIGINT, KeyAbort); + term_cbreak(); + } + + if (newBuf->sourcefile == NULL && uf->scheme != SCM_LOCAL) { + tmpf = tmpfname(TMPF_SRC, NULL); + src = fopen(tmpf->ptr, "w"); + if (src) + newBuf->sourcefile = tmpf->ptr; + } +#ifdef JP_CHARSET + if (newBuf->document_code != '\0') + code = newBuf->document_code; + else if (content_charset != '\0') + code = content_charset; + else + code = DocumentCode; + content_charset = '\0'; +#endif + + nlines = 0; + if (IStype(uf->stream) != IST_ENCODED) + uf->stream = newEncodedStream(uf->stream, uf->encoding); + while ((lineBuf2 = StrmyISgets(uf->stream))->length) { + if (src) + Strfputs(lineBuf2, src); + linelen += lineBuf2->length; + showProgress(&linelen, &trbyte); + lineBuf2 = convertLine(uf, lineBuf2, &code, PAGER_MODE); + if (squeezeBlankLine) { + if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') { + ++nlines; + continue; + } + pre_lbuf = lineBuf2->ptr[0]; + } + ++nlines; + if (showLineNum) { + Str tmp = Sprintf("%4d:", nlines); + Strcat(tmp, lineBuf2); + lineBuf2 = tmp; + } +#ifdef USE_NNTP + if (uf->scheme == SCM_NEWS) { + if (Str_news_endline(lineBuf2)) { + iseos(uf->stream) = TRUE; + break; + } + } +#endif /* USE_NNTP */ + Strchop(lineBuf2); + lineBuf2 = checkType(lineBuf2, propBuffer, +#ifdef ANSI_COLOR + colorBuffer, &check_color, +#endif + LINELEN); + addnewline(newBuf, lineBuf2->ptr, propBuffer, +#ifdef ANSI_COLOR + check_color ? colorBuffer : NULL, +#endif + lineBuf2->length, nlines); + } + _end: + if (fmInitialized) { + signal(SIGINT, prevtrap); + term_raw(); + } + newBuf->topLine = newBuf->firstLine; + newBuf->currentLine = newBuf->firstLine; + newBuf->trbyte = trbyte + linelen; +#ifdef JP_CHARSET + newBuf->document_code = code; +#endif /* JP_CHARSET */ + if (src) + fclose(src); + + return newBuf; +} + +/* + * saveBuffer: write buffer to file + */ + +void +saveBuffer(Buffer * buf, FILE * f) +{ + saveBufferDelNum(buf, f, FALSE); +} + +#ifndef KANJI_SYMBOLS +static Str +conv_rule(Line *l) +{ + Str tmp = NULL; + char *p = l->lineBuf, *ep = p + l->len; + Lineprop *pr = l->propBuf; + + for (; p < ep; p++, pr++) { + if (*pr & PC_RULE) { + if (tmp == NULL) { + tmp = Strnew_size(l->len); + Strcopy_charp_n(tmp, l->lineBuf, p - l->lineBuf); + } + Strcat_char(tmp, alt_rule[*p & 0xF]); + } else if (tmp != NULL) + Strcat_char(tmp, *p); + } + if (tmp) + return tmp; + else + return Strnew_charp_n(l->lineBuf, l->len); +} +#endif + +void +saveBufferDelNum(Buffer * buf, FILE * f, int del) +{ + Line *l = buf->firstLine; + Str tmp; + char *p; + +#ifndef KANJI_SYMBOLS + int is_html = FALSE; + + if (buf->type && ! strcasecmp(buf->type, "text/html")) + is_html = TRUE; +#endif + + pager_next: + for (; l != NULL; l = l->next) { +#ifndef KANJI_SYMBOLS + if (is_html) + tmp = conv_rule(l); + else +#endif + tmp = Strnew_charp_n(l->lineBuf, l->len); + if (del && (p = strchr(tmp->ptr, ':')) != NULL) + Strdelete(tmp, 0, p - tmp->ptr + 1); +#ifdef JP_CHARSET + tmp = conv_str(tmp, InnerCode, DisplayCode); +#endif + Strfputs(tmp, f); + if (Strlastchar(tmp) != '\n') + putc('\n', f); + } + if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) { + l = getNextPage(buf, PagerMax); + goto pager_next; + } +} + +static Buffer * +loadcmdout(char *cmd, + Buffer * (*loadproc) (URLFile *, Buffer *), + Buffer * defaultbuf) +{ + FILE *f, *popen(const char *, const char *); + Buffer *buf; + URLFile uf; + + if (cmd == NULL || *cmd == '\0') + return NULL; + f = popen(cmd, "r"); + if (f == NULL) + return NULL; + init_stream(&uf, SCM_UNKNOWN, newFileStream(f, (void (*)()) pclose)); + buf = loadproc(&uf, defaultbuf); + UFclose(&uf); + if (buf == NULL) + return NULL; + return buf; +} + +/* + * getshell: execute shell command and get the result into a buffer + */ +Buffer * +getshell(char *cmd) +{ + Buffer *buf; + Str bn; + buf = loadcmdout(cmd, loadBuffer, NULL); + buf->filename = cmd; + bn = Sprintf("%s %s", SHELLBUFFERNAME, cmd); + buf->buffername = bn->ptr; + return buf; +} + +/* + * getpipe: execute shell command and connect pipe to the buffer + */ +Buffer * +getpipe(char *cmd) +{ + FILE *f, *popen(const char *, const char *); + Buffer *buf; + Str bn; + + if (cmd == NULL || *cmd == '\0') + return NULL; + f = popen(cmd, "r"); + if (f == NULL) + return NULL; + buf = newBuffer(INIT_BUFFER_WIDTH); + buf->pagerSource = newFileStream(f, (void (*)()) pclose); + buf->filename = cmd; + bn = Sprintf("%s %s", PIPEBUFFERNAME, cmd); + buf->buffername = bn->ptr; + buf->bufferprop |= BP_PIPE; + return buf; +} + +/* + * Open pager buffer + */ +Buffer * +openPagerBuffer(InputStream stream, Buffer * buf) +{ + + if (buf == NULL) + buf = newBuffer(INIT_BUFFER_WIDTH); + buf->pagerSource = stream; + buf->buffername = getenv("MAN_PN"); + if (buf->buffername == NULL) + buf->buffername = PIPEBUFFERNAME; + buf->bufferprop |= BP_PIPE; +#ifdef JP_CHARSET + buf->document_code = DocumentCode; +#endif + buf->currentLine = buf->firstLine; + + return buf; +} + +Buffer * +openGeneralPagerBuffer(InputStream stream) +{ + Buffer *buf; + char *t = "text/plain"; + Buffer *t_buf = NULL; + URLFile uf; + + init_stream(&uf, SCM_UNKNOWN, stream); + +#ifdef JP_CHARSET + content_charset = '\0'; +#endif + if (SearchHeader) { + t_buf = newBuffer(INIT_BUFFER_WIDTH); + readHeader(&uf, t_buf, TRUE, NULL); + t = checkContentType(t_buf); + if (t == NULL) + t = "text/plain"; + if (t_buf) { + t_buf->topLine = t_buf->firstLine; + t_buf->currentLine = t_buf->lastLine; + } + SearchHeader = FALSE; + } + else if (DefaultType) { + t = DefaultType; + DefaultType = NULL; + } + if (!strcmp(t, "text/html")) { + buf = loadHTMLBuffer(&uf, t_buf); + buf->type = "text/html"; + } + else if (is_plain_text_type(t)) { + buf = openPagerBuffer(stream, t_buf); + buf->type = "text/plain"; + } + else { + if (doExternal(uf, "-", t, &buf, t_buf)) { + ; + } + else { /* unknown type is regarded as text/plain */ + buf = openPagerBuffer(stream, t_buf); + buf->type = "text/plain"; + } + } + buf->real_type = t; + buf->encoding = uf.encoding; + buf->currentURL.scheme = SCM_LOCAL; + buf->currentURL.file = "-"; + return buf; +} + +Line * +getNextPage(Buffer * buf, int plen) +{ + Line *l, *fl, *pl = buf->lastLine; + Line *rl = NULL; + int len, i, nlines = 0; + int linelen = buf->linelen, trbyte = buf->trbyte; + Str lineBuf2; + char pre_lbuf = '\0'; + URLFile uf; + char code; + int squeeze_flag = 0; +#ifdef ANSI_COLOR + int check_color; +#endif + + if (buf->pagerSource == NULL) + return NULL; + + if (fmInitialized) + crmode(); + if (pl != NULL) { + nlines = pl->real_linenumber; + pre_lbuf = *(pl->lineBuf); + if (showLineNum) { + char *p; + if ((p = strchr(pl->lineBuf, ':')) != NULL) + pre_lbuf = *(p + 1); + } + if (pre_lbuf == '\0') + pre_lbuf = '\n'; + } + +#ifdef JP_CHARSET + code = buf->document_code; +#endif + init_stream(&uf, SCM_UNKNOWN, NULL); + for (i = 0; i < plen; i++) { + lineBuf2 = StrmyISgets(buf->pagerSource); + if (lineBuf2->length == 0) { + /* Assume that `cmd == buf->filename' */ + if (buf->filename) + buf->buffername = Sprintf("%s %s", + CPIPEBUFFERNAME, buf->filename)->ptr; + else if (getenv("MAN_PN") == NULL) + buf->buffername = CPIPEBUFFERNAME; + buf->bufferprop |= BP_CLOSE; + trbyte += linelen; + linelen = 0; + break; + } + linelen += lineBuf2->length; + showProgress(&linelen, &trbyte); + lineBuf2 = convertLine(&uf, lineBuf2, &code, PAGER_MODE); + if (squeezeBlankLine) { + squeeze_flag = 0; + if (lineBuf2->ptr[0] == '\n' && pre_lbuf == '\n') { + ++nlines; + --i; + squeeze_flag = 1; + continue; + } + pre_lbuf = lineBuf2->ptr[0]; + } + ++nlines; + if (showLineNum) { + Str tmp = Sprintf("%4d:", nlines); + Strcat(tmp, lineBuf2); + lineBuf2 = tmp; + } + Strchop(lineBuf2); + lineBuf2 = checkType(lineBuf2, propBuffer, +#ifdef ANSI_COLOR + colorBuffer, &check_color, +#endif + LINELEN); + len = lineBuf2->length; + l = New(Line); + l->lineBuf = lineBuf2->ptr; + l->propBuf = New_N(Lineprop, len); + bcopy((void *) propBuffer, (void *) l->propBuf, len * sizeof(Lineprop)); +#ifdef ANSI_COLOR + if (check_color) { + l->colorBuf = New_N(Linecolor, len); + bcopy((void *) colorBuffer, (void *) l->colorBuf, len * sizeof(Linecolor)); + } else { + l->colorBuf = NULL; + } +#endif + l->len = len; + l->width = -1; + l->prev = pl; + if (squeezeBlankLine) { + l->real_linenumber = nlines; + l->linenumber = (pl == NULL ? nlines : pl->linenumber + 1); + } + else { + l->real_linenumber = l->linenumber = nlines; + } + if (pl == NULL) { + pl = l; + buf->firstLine = buf->topLine = buf->currentLine = l; + } + else { + pl->next = l; + pl = l; + } + if (rl == NULL) + rl = l; + if (nlines > PagerMax) { + fl = buf->firstLine; + buf->firstLine = fl->next; + fl->next->prev = NULL; + if (buf->topLine == fl) + buf->topLine = fl->next; + if (buf->currentLine == fl) + buf->currentLine = fl->next; + } + } + if (pl != NULL) + pl->next = NULL; + buf->lastLine = pl; + if (rl == NULL && squeeze_flag) { + rl = pl; + } + if (fmInitialized) + term_raw(); + buf->linelen = linelen; + buf->trbyte = trbyte; +#ifdef JP_CHARSET + buf->document_code = code; +#endif + return rl; +} + +static void +FTPhalfclose(InputStream stream) +{ + if (IStype(stream) == IST_FILE && file_of(stream)) { + Ftpfclose(file_of(stream)); + file_of(stream) = NULL; + } +} + +int +save2tmp(URLFile uf, char *tmpf) +{ + FILE *ff; + int check; + int linelen = 0, trbyte = 0; + MySignalHandler(*prevtrap) (); + static JMP_BUF env_bak; + + ff = fopen(tmpf, "wb"); + if (ff == NULL) { + /* fclose(f); */ + return -1; + } + bcopy(AbortLoading, env_bak, sizeof(JMP_BUF)); + if (SETJMP(AbortLoading) != 0) { + goto _end; + } + if (fmInitialized) { + prevtrap = signal(SIGINT, KeyAbort); + term_cbreak(); + } + check = 0; + current_content_length = 0; +#ifdef USE_NNTP + if (uf.scheme == SCM_NEWS) { + char c; + while (c = UFgetc(&uf), !iseos(uf.stream)) { + if (c == '\n') { + if (check == 0) + check++; + else if (check == 3) + break; + } + else if (c == '.' && check == 1) + check++; + else if (c == '\r' && check == 2) + check++; + else + check = 0; + putc(c, ff); + linelen += sizeof(c); + showProgress(&linelen, &trbyte); + } + } + else +#endif /* USE_NNTP */ + { + Str buf = Strnew_size(SAVE_BUF_SIZE); + while (UFread(&uf, buf, SAVE_BUF_SIZE)) { + Strfputs(buf, ff); + linelen += buf->length; + showProgress(&linelen, &trbyte); + } + } + _end: + bcopy(env_bak, AbortLoading, sizeof(JMP_BUF)); + if (fmInitialized) { + term_raw(); + signal(SIGINT, prevtrap); + } + fclose(ff); + if (uf.scheme == SCM_FTP) + FTPhalfclose(uf.stream); + return 0; +} + +int +doExternal(URLFile uf, char *path, char *type, Buffer **bufp, Buffer *defaultbuf) +{ + Str tmpf, command; + struct mailcap *mcap; + int stat; + Buffer *buf = NULL; + + if (!(mcap = searchExtViewer(type))) + return 0; + + tmpf = tmpfname(TMPF_DFL, NULL); + + if (mcap->nametemplate) { + Str tmp = unquote_mailcap(mcap->nametemplate, NULL, tmpf->ptr, NULL); + if (Strncmp(tmpf, tmp, tmpf->length) == 0) { + tmpf = tmp; + goto _save; + } + } + if (uf.ext && *uf.ext) { + Strcat_charp(tmpf, uf.ext); + } + _save: + if (save2tmp(uf, tmpf->ptr) < 0) + return 0; + command = unquote_mailcap(mcap->viewer, type, tmpf->ptr, &stat); +#ifndef __EMX__ + if (!(stat & MCSTAT_REPNAME)) { + Str tmp = Sprintf("(%s) < %s", command->ptr, tmpf->ptr); + command = tmp; + } +#endif + if (mcap->flags & (MAILCAP_HTMLOUTPUT|MAILCAP_COPIOUSOUTPUT)) { + if (defaultbuf == NULL) + defaultbuf = newBuffer(INIT_BUFFER_WIDTH); + defaultbuf->sourcefile = tmpf->ptr; + } + if (mcap->flags & MAILCAP_HTMLOUTPUT) { + buf = loadcmdout(command->ptr, loadHTMLBuffer, defaultbuf); + if (buf) + buf->type = "text/html"; + } + else if (mcap->flags & MAILCAP_COPIOUSOUTPUT) { + buf = loadcmdout(command->ptr, loadBuffer, defaultbuf); + if (buf) + buf->type = "text/plain"; + } + else { + if (mcap->flags & MAILCAP_NEEDSTERMINAL || !BackgroundExtViewer) { + fmTerm(); + mySystem(command->ptr, 0); + fmInit(); + if (Currentbuf) + displayBuffer(Currentbuf, B_FORCE_REDRAW); + } else { + mySystem(command->ptr, 1); + } + buf = NO_BUFFER; + } + if (buf && buf != NO_BUFFER) { + buf->filename = path; + if (buf->buffername == NULL || buf->buffername[0] == '\0') + buf->buffername = lastFileName(path); + buf->edit = mcap->edit; + } + *bufp = buf; + pushText(fileToDelete, tmpf->ptr); + return 1; +} + +static int +_MoveFile(char *path1, char *path2) +{ + InputStream f1; + FILE *f2; + int is_pipe; + int linelen = 0, trbyte = 0; + Str buf; + + f1 = openIS(path1); + if (f1 == NULL) + return -1; + if (*path2 == '|' && PermitSaveToPipe) { + is_pipe = TRUE; + f2 = popen(path2 + 1, "w"); + } + else { + is_pipe = FALSE; + f2 = fopen(path2, "wb"); + } + if (f2 == NULL) { + ISclose(f1); + return -1; + } + current_content_length = 0; + buf = Strnew_size(SAVE_BUF_SIZE); + while (ISread(f1, buf, SAVE_BUF_SIZE)) { + Strfputs(buf, f2); + linelen += buf->length; + showProgress(&linelen, &trbyte); + } + ISclose(f1); + if (is_pipe) + pclose(f2); + else + fclose(f2); + return 0; +} + +void +doFileCopy(char *tmpf, char *defstr) +{ + Str msg; + char filen[256]; + char *p, *q; + + if (fmInitialized) { + p = searchKeyData(); + if (p == NULL || *p == '\0') { + p = inputLineHist("(Download)Save file to: ", + defstr, IN_COMMAND, SaveHist); + if (p == NULL || *p == '\0') + return; + } + if (*p != '|' || !PermitSaveToPipe) { + p = expandName(p); + if (checkOverWrite(p) < 0) + return; + } + if (checkCopyFile(tmpf, p) < 0) { + msg = Sprintf("Can't copy. %s and %s are identical.", tmpf, p); + disp_err_message(msg->ptr, FALSE); + return; + } + if (_MoveFile(tmpf, p) < 0) { + msg = Sprintf("Can't save to %s", p); + disp_err_message(msg->ptr, FALSE); + } + } + else { + q = searchKeyData(); + if (q == NULL || *q == '\0') { + printf("(Download)Save file to: "); + fflush(stdout); + p = fgets(filen, sizeof(filen), stdin); + if (p == NULL || filen[0] == '\0') + return; + q = filen; + } + for (p = q + strlen(q) - 1; IS_SPACE(*p); p--); + *(p + 1) = '\0'; + if (*q == '\0') + return; + p = q; + if (*p != '|' || !PermitSaveToPipe) { + p = expandName(p); + if (checkOverWrite(p) < 0) + return; + } + if (checkCopyFile(tmpf, p) < 0) { + printf("Can't copy. %s and %s are identical.", tmpf, p); + return; + } + if (_MoveFile(tmpf, p) < 0) { + printf("Can't save to %s\n", p); + } + } +} + +void +doFileMove(char *tmpf, char *defstr) +{ + doFileCopy(tmpf, defstr); + unlink(tmpf); +} + +void +doFileSave(URLFile uf, char *defstr) +{ + Str msg; + char filen[256]; + char *p, *q; + + if (fmInitialized) { + p = searchKeyData(); + if (p == NULL || *p == '\0') { + p = inputLineHist("(Download)Save file to: ", + defstr, IN_FILENAME, SaveHist); + if (p == NULL || *p == '\0') + return; + } + if (checkOverWrite(p) < 0) + return; + if (checkSaveFile(uf.stream, p) < 0) { + msg = Sprintf("Can't save. Load file and %s are identical.", p); + disp_err_message(msg->ptr, FALSE); + return; + } + if (save2tmp(uf, p) < 0) { + msg = Sprintf("Can't save to %s", p); + disp_err_message(msg->ptr, FALSE); + } + } + else { + q = searchKeyData(); + if (q == NULL || *q == '\0') { + printf("(Download)Save file to: "); + fflush(stdout); + p = fgets(filen, sizeof(filen), stdin); + if (p == NULL || filen[0] == '\0') + return; + q = filen; + } + for (p = q + strlen(q) - 1; IS_SPACE(*p); p--); + *(p + 1) = '\0'; + if (*q == '\0') + return; + p = expandName(q); + if (checkOverWrite(p) < 0) + return; + if (checkSaveFile(uf.stream, p) < 0) { + printf("Can't save. Load file and %s are identical.", p); + return; + } + if (save2tmp(uf, p) < 0) { + printf("Can't save to %s\n", p); + } + } +} + +int +checkCopyFile(char *path1, char *path2) +{ + struct stat st1, st2; + + if (*path2 == '|' && PermitSaveToPipe) + return 0; + if ((stat(path1, &st1) == 0) && (stat(path2, &st2) == 0)) + if (st1.st_ino == st2.st_ino) + return -1; + return 0; +} + +int +checkSaveFile(InputStream stream, char *path2) +{ + struct stat st1, st2; + int des = ISfileno(stream); + + if (des < 0) + return 0; + if (*path2 == '|' && PermitSaveToPipe) + return 0; + if ((fstat(des, &st1) == 0) && (stat(path2, &st2) == 0)) + if (st1.st_ino == st2.st_ino) + return -1; + return 0; +} + +int +checkOverWrite(char *path) +{ + struct stat st; + char buf[2]; + char *ans = NULL; + + if (stat(path, &st) < 0) + return 0; + if (fmInitialized) { + ans = inputStr("File exists. Overwrite? (y or n)", ""); + } + else { + printf("File exists. Overwrite? (y or n)"); + ans = fgets(buf, 2, stdin); + } + if (ans != NULL && (*ans == '\0' || tolower(*ans) == 'y')) + return 0; + else + return -1; +} + +static void +sig_chld(int signo) +{ + int stat; +#ifdef HAVE_WAITPID + pid_t pid; + + while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) { + ; + } +#elif HAVE_WAIT3 + int pid; + + while ((pid = wait3(&stat, WNOHANG, NULL)) > 0) { + ; + } +#else + wait(&stat); +#endif + return; +} + +#ifdef __EMX__ +#define GUNZIP_CMD "gzip" +#define BUNZIP2_CMD "bzip2" +#else /* not __EMX__ */ +#define GUNZIP_CMD "gunzip" +#define BUNZIP2_CMD "bunzip2" +#endif /* not __EMX__ */ +#define INFLATE_CMD "inflate" +#define GUNZIP_NAME "gunzip" +#define BUNZIP2_NAME "bunzip2" +#define INFLATE_NAME "inflate" + +void +gunzip_stream(URLFile *uf) +{ + int pid1; + int fd1[2]; + char *expand_cmd = GUNZIP_CMD; + char *expand_name = GUNZIP_NAME; + char *tmpf = NULL; + + switch (uf->compression) { + case CMP_COMPRESS: + case CMP_GZIP: + expand_cmd = GUNZIP_CMD; + expand_name = GUNZIP_NAME; + break; + case CMP_BZIP2: + expand_cmd = BUNZIP2_CMD; + expand_name = BUNZIP2_NAME; + break; + case CMP_DEFLATE: + expand_cmd = INFLATE_CMD; + expand_name = INFLATE_NAME; + break; + } + uf->compression = CMP_NOCOMPRESS; + + if (pipe(fd1) < 0) { + UFclose(uf); + return; + } + + if (uf->scheme != SCM_HTTP && uf->scheme != SCM_LOCAL) { + tmpf = tmpfname(TMPF_DFL, NULL)->ptr; + if (save2tmp(*uf, tmpf) < 0) { + UFclose(uf); + return; + } + if (uf->scheme != SCM_FTP) + UFclose(uf); + pushText(fileToDelete, tmpf); + } + +#ifdef SIGCHLD + signal(SIGCHLD, sig_chld); +#endif + flush_tty(); + /* fd1[0]: read, fd1[1]: write */ + if ((pid1 = fork()) == 0) { + signal(SIGINT, SIG_DFL); + close(fd1[0]); + if (tmpf) { +#ifdef __CYGWIN__ + int tmpfd = open(tmpf, O_RDONLY|O_BINARY); +#else + int tmpfd = open(tmpf, O_RDONLY); +#endif + if (tmpfd < 0) { + close(fd1[1]); + exit(1); + } + dup2(tmpfd, 0); + } + else { + /* child */ + int pid2; + int fd2[2]; +#ifdef SIGCHLD + signal(SIGCHLD, sig_chld); +#endif + if (fmInitialized) { + close_tty(); + fmInitialized = FALSE; + } + if (pipe(fd2) < 0) { + close(fd1[1]); + UFclose(uf); + exit(1); + } + if ((pid2 = fork()) == 0) { + /* child */ + Str buf = Strnew_size(SAVE_BUF_SIZE); + close(fd2[0]); + while (UFread(uf, buf, SAVE_BUF_SIZE)) { + if (write(fd2[1], buf->ptr, buf->length) < 0) { + close(fd2[1]); + exit(0); + } + } + close(fd2[1]); + exit(0); + } + close(fd2[1]); + dup2(fd2[0], 0); + } + dup2(fd1[1], 1); + execlp(expand_cmd, expand_name, NULL); + exit(0); + } + close(fd1[1]); + if (tmpf == NULL) + UFclose(uf); + uf->stream = newFileStream(fdopen(fd1[0], "rb"), (void (*)()) pclose); +} + +static FILE * +lessopen_stream(char *path) +{ + char *lessopen; + FILE *fp; + + lessopen = getenv("LESSOPEN"); + if (lessopen == NULL) { + return NULL; + } + if (lessopen[0] == '\0') { + return NULL; + } + + if (lessopen[0] == '|') { + /* pipe mode */ + Str tmpf; + int c; + + ++lessopen; + tmpf = Sprintf(lessopen, path); + fp = popen(tmpf->ptr, "r"); + if (fp == NULL) { + return NULL; + } + c = getc(fp); + if (c == EOF) { + fclose(fp); + return NULL; + } + ungetc(c, fp); + } + else { + /* filename mode */ + /* not supported m(__)m */ + fp = NULL; + } + return fp; +} + +#if 0 +void +reloadBuffer(Buffer * buf) +{ + URLFile uf; + + if (buf->sourcefile == NULL || + buf->pagerSource != NULL) + return; + init_stream(&uf, SCM_UNKNOWN, NULL); + examineFile(buf->sourcefile, &uf); + if (uf.stream == NULL) + return; + is_redisplay = TRUE; + buf->allLine = 0; + buf->href = NULL; + buf->name = NULL; + buf->img = NULL; + buf->formitem = NULL; + if (!strcasecmp(buf->type, "text/html")) + loadHTMLBuffer(&uf, buf); + else + loadBuffer(&uf, buf); + UFclose(&uf); + is_redisplay = FALSE; +} +#endif + +#ifdef JP_CHARSET +static char +guess_charset(char *p) +{ + Str c = Strnew_size(strlen(p)); + if (strncasecmp(p, "x-", 2) == 0) + p += 2; + while (*p != '\0') { + if (*p != '-' && *p != '_') + Strcat_char(c, tolower(*p)); + p++; + } + if (strncmp(c->ptr, "euc", 3) == 0) + return CODE_EUC; + if (strncmp(c->ptr, "shiftjis", 8) == 0 || + strncmp(c->ptr, "sjis", 4) == 0) + return CODE_SJIS; + if (strncmp(c->ptr, "iso2022jp", 9) == 0 || + strncmp(c->ptr, "jis", 3) == 0) + return CODE_JIS_n; + return CODE_ASCII; +} +#endif + +char * +guess_save_name(char *file) +{ + char *p = NULL, *s; + + if (file != NULL) + p = mybasename(file); + if (p == NULL || *p == '\0') + return DEF_SAVE_FILE; + s = p; + if (*p == '#') + p++; + while (*p != '\0') { + if ((*p == '#' && *(p + 1) != '\0') || *p == '?') { + *p = '\0'; + break; + } + p++; + } + return s; +} + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ @@ -0,0 +1,814 @@ +/* $Id: fm.h,v 1.1 2001/11/08 05:14:53 a-ito Exp $ */ +/* + * w3m: WWW wo Miru utility + * + * by A.ITO Feb. 1995 + * + * You can use,copy,modify and distribute this program without any permission. + */ + +#ifndef FM_H +#define FM_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include "config.h" +#include "history.h" + +#ifdef MENU +#define MENU_SELECT +#endif /* MENU */ + +#ifndef COLOR +#undef ANSI_COLOR +#endif + +#include "ctrlcode.h" +#include "html.h" +#include "gc.h" +#include "Str.h" +#include "form.h" +#include "frame.h" +#include "parsetag.h" +#include "parsetagx.h" +#include "func.h" +#include "menu.h" +#include "textlist.h" +#include "funcname1.h" +#include "terms.h" + +#ifdef NOBCOPY +void bcopy(void *, void *, int); +void bzero(void *, int); +#else /* not NOBCOPY */ +#include <string.h> +#endif /* not NOBCOPY */ + +#ifdef MAINPROGRAM +#define global +#define init(x) =(x) +#else /* not MAINPROGRAM */ +#define global extern +#define init(x) +#endif /* not MAINPROGRAM */ + +#if LANG == JA +#define JP_CHARSET +#endif /* LANG == JA */ + +/* + * Constants. + */ +#define LINELEN 4096 /* Maximum line length */ +#define PAGER_MAX_LINE 10000 /* Maximum line kept as pager */ +#define FNLEN 80 + +#define DEFAULT_PIXEL_PER_CHAR 8.0 /* arbitrary */ +#define MINIMUM_PIXEL_PER_CHAR 4.0 +#define MAXIMUM_PIXEL_PER_CHAR 32.0 + +#ifdef FALSE +#undef FALSE +#endif + +#ifdef TRUE +#undef TRUE +#endif + +#define FALSE 0 +#define TRUE 1 + +#ifdef USE_COOKIE +#define PERHAPS 2 +#endif + +#define SHELLBUFFERNAME "*Shellout*" +#define PIPEBUFFERNAME "*stream*" +#define CPIPEBUFFERNAME "*stream(closed)*" +#ifdef DICT +#define DICTCMD "w3mdict" +#define DICTBUFFERNAME "*dictionary*" +#endif /* DICT */ + +/* + * Line Property + */ +/* Character type */ +#define PC_ASCII 0x0000 +#define PC_CTRL 0x2000 + +#ifdef JP_CHARSET +#define PC_KANJI1 0x4000 +#define PC_KANJI2 0x8000 +#define PC_KANJI (PC_KANJI1|PC_KANJI2) +#define P_CHARTYPE (PC_ASCII|PC_CTRL|PC_KANJI) +#else /* ISO-8859-1 charset (not JP_CHARSET) */ +#define P_CHARTYPE (PC_ASCII|PC_CTRL) +#endif /* not JP_CHARSET */ +#if 0 +#define GET_PCTYPE(c) ((GET_MYCTYPE(c)&MYCTYPE_CNTRL)<<13) +#else +#define GET_PCTYPE(c) ((GET_MYCTYPE(c)&MYCTYPE_CNTRL)?PC_CTRL:PC_ASCII) +#endif + +#ifndef KANJI_SYMBOLS +#define PC_RULE 0x1000 +#endif /* not KANJI_SYMBOLS */ + +/* Effect ( standout/underline ) */ +#define P_EFFECT 0x01fe +#define PE_NORMAL 0x00 +#define PE_UNDER 0x02 +#define PE_STAND 0x04 +#define PE_BOLD 0x08 +#define PE_ANCHOR 0x10 +#define PE_EMPH 0x08 +#define PE_IMAGE 0x20 +#define PE_FORM 0x40 +#define PE_ACTIVE 0x80 +#define PE_VISITED 0x0100 + +/* Mark */ +#define PM_MARK 0x01 + +#define CharType(c) ((c)&P_CHARTYPE) +#ifdef KANJI_SYMBOLS +#define CharEffect(c) ((c)&P_EFFECT) +#else /* not KANJI_SYMBOLS */ +#define CharEffect(c) ((c)&(P_EFFECT|PC_RULE)) +#endif /* not KANJI_SYMBOLS */ +#define SetCharType(v,c) ((v)=(((v)&~P_CHARTYPE)|(c))) + + +#define COLPOS(l,c) calcPosition(l->lineBuf,l->propBuf,l->len,c,0,CP_AUTO) +#define IS_UNPRINTABLE_CONTROL(c,m) (CharType(m)==PC_CTRL&&(c)!=CTRL_I&&(c)!=CTRL_J) +#ifdef JP_CHARSET +#define IS_UNPRINTABLE_ASCII(c,m) (!IS_ASCII(c)&&CharType(m)==PC_ASCII) +#else +#define IS_UNPRINTABLE_ASCII(c,m) (!IS_LATIN1(c)) +#endif + +/* Flags for displayBuffer() */ +#define B_NORMAL 0 +#define B_FORCE_REDRAW 1 +#define B_REDRAW 2 +#define B_SCROLL 3 + +/* Buffer Property */ +#define BP_NORMAL 0x0 +#define BP_PIPE 0x1 +#define BP_FRAME 0x2 +#define BP_SOURCE 0x4 +#define BP_INTERNAL 0x8 +#define BP_NO_URL 0x10 +#define BP_REDIRECTED 0x20 +#define BP_CLOSE 0x40 + +/* Link Buffer */ +#define LB_NOLINK -1 +#define LB_FRAME 0 /* rFrame() */ +#define LB_N_FRAME 1 +#define LB_INFO 2 /* pginfo() */ +#define LB_N_INFO 3 +#define LB_SOURCE 4 /* vwSrc() */ +#define LB_N_SOURCE LB_SOURCE +#define MAX_LB 5 + +#ifdef MAINPROGRAM +int REV_LB[MAX_LB] = +{ + LB_N_FRAME, LB_FRAME, LB_N_INFO, LB_INFO, LB_N_SOURCE, +}; +#else /* not MAINPROGRAM */ +extern int REV_LB[]; +#endif /* not MAINPROGRAM */ + +/* mark URL, Message-ID */ +#define CHK_URL 1 +#define CHK_NMID 2 + +/* Flags for calcPosition() */ +#define CP_AUTO 0 +#define CP_FORCE 1 + +/* Completion status. */ +#define CPL_OK 0 +#define CPL_AMBIG 1 +#define CPL_FAIL 2 +#define CPL_MENU 3 + +#define CPL_NEVER 0x0 +#define CPL_OFF 0x1 +#define CPL_ON 0x2 +#define CPL_ALWAYS 0x4 +#define CPL_URL 0x8 + +/* Flags for inputLine() */ +#define IN_STRING 0x10 +#define IN_FILENAME 0x20 +#define IN_PASSWORD 0x40 +#define IN_COMMAND 0x80 +#define IN_URL 0x100 + +/* + * Macros. + */ + +#define inputLine(p,d,f) inputLineHist(p,d,f,NULL) +#define inputStr(p,d) inputLine(p,d,IN_STRING) +#define inputStrHist(p,d,h) inputLineHist(p,d,IN_STRING,h) +#define inputFilename(p,d) inputLine(p,d,IN_FILENAME) +#define inputFilenameHist(p,d,h) inputLineHist(p,d,IN_FILENAME,h) + +#define free(x) GC_free(x) /* let GC do it. */ + +#ifdef __EMX__ +#define STRCASECMP +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif /* __EMX__ */ + + +#define SKIP_BLANKS(p) {while(*(p)&&IS_SPACE(*(p)))(p)++;} +#define SKIP_NON_BLANKS(p) {while(*(p)&&!IS_SPACE(*(p)))(p)++;} +#define IS_ENDL(c) ((c)=='\0'||(c)=='\r'||(c)=='\n') +#define IS_ENDT(c) (IS_ENDL(c)||(c)==';') + +#define bpcmp(a,b) \ + (((a).line - (b).line) ? ((a).line - (b).line) : ((a).pos - (b).pos)) + +#define RELATIVE_WIDTH(w) (((w)>=0)?(int)((w)/pixel_per_char):(w)) +#define REAL_WIDTH(w,limit) (((w)>=0)?(int)((w)/pixel_per_char):-(w)*(limit)/100) + +#define EOL(l) (&(l)->ptr[(l)->length]) +#define IS_EOL(p,l) ((p)==&(l)->ptr[(l)->length]) + +/* + * Types. + */ + +typedef unsigned short Lineprop; +#ifdef ANSI_COLOR +typedef unsigned char Linecolor; +#endif + +typedef struct _MapList { + Str name; + TextList *urls; + TextList *alts; + struct _MapList *next; +} MapList; + +typedef struct _Line { + char *lineBuf; + Lineprop *propBuf; +#ifdef ANSI_COLOR + Linecolor *colorBuf; +#endif + struct _Line *next; + struct _Line *prev; + short len; + short width; + long linenumber; /* on buffer */ + long real_linenumber; /* on file */ + unsigned short usrflags; +} Line; + +typedef struct { + int line; + short pos; +} BufferPoint; + +typedef struct _anchor { + char *url; + char *target; + char *referer; + BufferPoint start; + BufferPoint end; + int hseq; +} Anchor; + +#define NO_REFERER ((char*)-1) + +typedef struct _anchorList { + Anchor *anchors; + int nanchor; + int anchormax; + int acache; +} AnchorList; + +typedef struct { + BufferPoint *marks; + int nmark; + int markmax; + int prevhseq; +} HmarkerList; + +typedef struct _Buffer { + char *filename; + char *buffername; + Line *firstLine; + Line *topLine; + Line *currentLine; + Line *lastLine; + struct _Buffer *nextBuffer; + struct _Buffer *linkBuffer[MAX_LB]; + short width; + short height; + char *type; + char *real_type; + char encoding; + int allLine; + short bufferprop; + short currentColumn; + short cursorX; + short cursorY; + short pos; + short visualpos; + InputStream pagerSource; + AnchorList *href; + AnchorList *name; + AnchorList *img; + AnchorList *formitem; + FormList *formlist; + MapList *maplist; + HmarkerList *hmarklist; + ParsedURL currentURL; + ParsedURL *baseURL; + char *baseTarget; + int real_scheme; + char *sourcefile; + struct frameset *frameset; + struct frameset_queue *frameQ; + int *clone; + int linelen; + int trbyte; + char check_url; +#ifdef JP_CHARSET + char document_code; +#endif /* JP_CHARSET */ + TextList *document_header; + FormItemList *form_submit; + char *savecache; + char *edit; +#ifdef USE_SSL + char *ssl_certificate; +#endif +} Buffer; + +#define NO_BUFFER ((Buffer*)1) + +#define RB_STACK_SIZE 10 + +#define TAG_STACK_SIZE 10 + +#define FONT_STACK_SIZE 5 + +#define FONTSTAT_SIZE 4 + +#define INIT_BUFFER_WIDTH (COLS-1) + +typedef struct { + int pos; + int len; + int tlen; + long flag; + Str anchor; + Str anchor_target; + short anchor_hseq; + Str img_alt; + char fontstat[FONTSTAT_SIZE]; + short nobr_level; + Lineprop prev_ctype; + char init_flag; +} Breakpoint; + +struct readbuffer { + Str line; + Lineprop cprop; + short pos; + int prevchar; + long flag; + long flag_stack[RB_STACK_SIZE]; + int flag_sp; + int status; + Str ignore_tag; + short table_level; + short nobr_level; + Str anchor; + Str anchor_target; + short anchor_hseq; + Str img_alt; + char fontstat[FONTSTAT_SIZE]; + char fontstat_stack[FONT_STACK_SIZE][FONTSTAT_SIZE]; + int fontstat_sp; + Lineprop prev_ctype; + Breakpoint bp; + struct cmdtable *tag_stack[TAG_STACK_SIZE]; + int tag_sp; +}; + +#define in_bold fontstat[0] +#define in_under fontstat[1] +#define in_stand fontstat[2] + +#define RB_PRE 0x01 +#define RB_XMPMODE 0x02 +#define RB_LSTMODE 0x04 +#define RB_PLAIN 0x08 +#define RB_LEFT 0x00 +#define RB_CENTER 0x10 +#define RB_RIGHT 0x20 +#define RB_ALIGN (RB_CENTER | RB_RIGHT) +#define RB_NOBR 0x40 +#define RB_P 0x80 +#define RB_PRE_INT 0x100 +#define RB_PREMODE (RB_PRE | RB_PRE_INT) +#define RB_SPECIAL (RB_PRE|RB_XMPMODE|RB_LSTMODE|RB_PLAIN|RB_NOBR|RB_PRE_INT) +#define RB_PLAINMODE (RB_XMPMODE|RB_LSTMODE|RB_PLAIN) + +#define RB_IN_DT 0x200 +#define RB_INTXTA 0x400 +#define RB_INSELECT 0x800 +#define RB_IGNORE 0x1000 +#define RB_INSEL 0x2000 +#define RB_IGNORE_P 0x4000 +#define RB_TITLE 0x8000 +#define RB_NFLUSHED 0x10000 + +#ifdef FORMAT_NICE +#define RB_FILL 0x200000 +#endif /* FORMAT_NICE */ + +#define RB_GET_ALIGN(obuf) ((obuf)->flag&RB_ALIGN) +#define RB_SET_ALIGN(obuf,align) {(obuf)->flag &= ~RB_ALIGN; (obuf)->flag |= (align); } +#define RB_SAVE_FLAG(obuf) {\ + if ((obuf)->flag_sp < RB_STACK_SIZE) \ + (obuf)->flag_stack[(obuf)->flag_sp++] = RB_GET_ALIGN(obuf); \ +} +#define RB_RESTORE_FLAG(obuf) {\ + if ((obuf)->flag_sp > 0) \ + RB_SET_ALIGN(obuf,(obuf)->flag_stack[--(obuf)->flag_sp]); \ +} + +/* status flags */ +#define R_ST_NORMAL 0 /* normal */ +#define R_ST_TAG0 1 /* within tag, just after < */ +#define R_ST_TAG 2 /* within tag */ +#define R_ST_QUOTE 3 /* within single quote */ +#define R_ST_DQUOTE 4 /* within double quote */ +#define R_ST_EQL 5 /* = */ +#define R_ST_AMP 6 /* within ampersand quote */ +#define R_ST_CMNT1 7 /* <! */ +#define R_ST_CMNT2 8 /* <!- */ +#define R_ST_CMNT 9 /* within comment */ +#define R_ST_NCMNT1 10 /* comment - */ +#define R_ST_NCMNT2 11 /* comment -- */ +#define R_ST_NCMNT3 12 /* comment -- space */ +#define R_ST_IRRTAG 13 /* within irregular tag */ + +#define ST_IS_REAL_TAG(s) ((s)==R_ST_TAG||(s)==R_ST_TAG0||(s)==R_ST_EQL) +#define ST_IS_COMMENT(s) ((s)>=R_ST_CMNT1) +#define ST_IS_TAG(s) ((s)!=R_ST_NORMAL&&(s)!=R_ST_AMP&&!ST_IS_COMMENT(s)) + +/* is this '<' really means the beginning of a tag? */ +#define REALLY_THE_BEGINNING_OF_A_TAG(p) \ + (IS_ALPHA(p[1]) || p[1] == '/' || p[1] == '!' || p[1] == '?' || p[1] == '\0' || p[1] == '_') + +/* flags for loadGeneralFile */ +#define RG_NOCACHE 1 +#define RG_FRAME 2 + +struct html_feed_environ { + struct readbuffer *obuf; + TextLineList *buf; + FILE *f; + Str tagbuf; + int limit; + int maxlimit; + struct environment *envs; + int nenv; + int envc; + int envc_real; + char *title; + int blank_lines; +}; + +struct auth_cookie { + Str host; + Str realm; + Str cookie; + struct auth_cookie *next; +}; + +#ifdef USE_COOKIE +struct portlist { + unsigned short port; + struct portlist *next; +}; + +struct cookie { + ParsedURL url; + Str name; + Str value; + time_t expires; + Str path; + Str domain; + Str comment; + Str commentURL; + struct portlist *portl; + char version; + char flag; + struct cookie *next; +}; +#define COO_USE 1 +#define COO_SECURE 2 +#define COO_DOMAIN 4 +#define COO_PATH 8 +#define COO_DISCARD 16 +#define COO_OVERRIDE 32 /* user chose to override security checks */ + +#define COO_OVERRIDE_OK 32 /* flag to specify that an error is overridable */ + /* version 0 refers to the original cookie_spec.html */ + /* version 1 refers to RFC 2109 */ + /* version 1' refers to the Internet draft to obsolete RFC 2109 */ +#define COO_EINTERNAL (1) /* unknown error; probably forgot to convert "return 1" in cookie.c */ +#define COO_ETAIL (2 | COO_OVERRIDE_OK) /* tail match failed (version 0) */ +#define COO_ESPECIAL (3) /* special domain check failed (version 0) */ +#define COO_EPATH (4) /* Path attribute mismatch (version 1 case 1) */ +#define COO_ENODOT (5 | COO_OVERRIDE_OK) /* no embedded dots in Domain (version 1 case 2.1) */ +#define COO_ENOTV1DOM (6 | COO_OVERRIDE_OK) /* Domain does not start with a dot (version 1 case 2.2) */ +#define COO_EDOM (7 | COO_OVERRIDE_OK) /* domain-match failed (version 1 case 3) */ +#define COO_EBADHOST (8 | COO_OVERRIDE_OK) /* dot in matched host name in FQDN (version 1 case 4) */ +#define COO_EPORT (9) /* Port match failed (version 1' case 5) */ +#define COO_EMAX COO_EPORT +#endif /* USE_COOKIE */ + +/* modes for align() */ + +#define ALIGN_CENTER 0 +#define ALIGN_LEFT 1 +#define ALIGN_RIGHT 2 + +#define VALIGN_MIDDLE 0 +#define VALIGN_TOP 1 +#define VALIGN_BOTTOM 2 + +typedef struct http_request { + char command; + char flag; + char *referer; + FormList *request; +} HRequest; + +#define HR_COMMAND_GET 0 +#define HR_COMMAND_POST 1 +#define HR_COMMAND_CONNECT 2 +#define HR_COMMAND_HEAD 3 + +#define HR_FLAG_LOCAL 1 + +#define HTST_UNKNOWN 255 +#define HTST_MISSING 254 +#define HTST_NORMAL 0 +#define HTST_CONNECT 1 + +#define TMPF_DFL 0 +#define TMPF_SRC 1 +#define TMPF_FRAME 2 +#define TMPF_CACHE 3 +#define MAX_TMPF_TYPE 4 + +#define set_no_proxy(domains) (NO_proxy_domains=make_domain_list(domains)) + +/* + * Globals. + */ + +extern int LINES, COLS; +#if defined(CYGWIN) && LANG == JA +#define LASTLINE (LINES-2) +#else /* not defined(CYGWIN) && LANG == JA */ +#define LASTLINE (LINES-1) +#endif /* not defined(CYGWIN) && LANG == JA */ + +global int Tabstop init(8); +global int ShowEffect init(TRUE); +global int PagerMax init(PAGER_MAX_LINE); +#ifdef JP_CHARSET +global char InnerCode init(CODE_INNER_EUC); /* use EUC-JP internally; do not change */ +#endif /* JP_CHARSET */ + +global char SearchHeader init(FALSE); +global char *DefaultType init(NULL); +global char RenderFrame init(FALSE); +global char TargetSelf init(FALSE); +global char PermitSaveToPipe init(FALSE); + +global char fmInitialized init(FALSE); + +extern char GlobalKeymap[]; +extern char EscKeymap[]; +extern char EscBKeymap[]; +extern char EscDKeymap[]; +#ifdef __EMX__ +extern char PcKeymap[]; +#endif +extern FuncList w3mFuncList[]; +extern KeyList w3mKeyList; + +global char *HTTP_proxy init(NULL); +#ifdef USE_GOPHER +global char *GOPHER_proxy init(NULL); +#endif /* USE_GOPHER */ +global char *FTP_proxy init(NULL); +global ParsedURL HTTP_proxy_parsed; +#ifdef USE_GOPHER +global ParsedURL GOPHER_proxy_parsed; +#endif /* USE_GOPHER */ +global ParsedURL FTP_proxy_parsed; +global char *NO_proxy init(NULL); +global int NOproxy_netaddr init(TRUE); +#ifdef INET6 +global int DNS_order init(0); +extern int ai_family_order_table[3][3]; /* XXX */ +#endif /* INET6 */ +global TextList *NO_proxy_domains; +global int Do_not_use_proxy init(FALSE); +global int Do_not_use_ti_te init(FALSE); + +global char *document_root init(NULL); +global char *personal_document_root init(NULL); +global char *cgi_bin init(NULL); +global char *index_file init(NULL); + +global char *CurrentDir; +global Buffer *Currentbuf; +global Buffer *Firstbuf; +global int CurrentKey; +global char *CurrentKeyData; +#ifdef MENU +global char *CurrentMenuData; +#endif +extern char *ullevel[]; + +extern char *version; + +global int w3m_debug; +global int w3m_halfdump init(FALSE); +global int w3m_halfload init(FALSE); + +#ifdef COLOR +global int useColor init(TRUE); +global int basic_color init(8); /* don't change */ +global int anchor_color init(4); /* blue */ +global int image_color init(2); /* green */ +global int form_color init(1); /* red */ +#ifdef BG_COLOR +global int bg_color init(8); /* don't change */ +#endif /* BG_COLOR */ +global int useActiveColor init(FALSE); +global int active_color init(6); /* cyan */ +global int useVisitedColor init(FALSE); +global int visited_color init(5); /* magenta */ +#endif /* COLOR */ +global int confirm_on_quit init(TRUE); +global int displayLink init(FALSE); +global int retryAsHttp init(TRUE); +global int showLineNum init(FALSE); +global char *Editor init(DEF_EDITOR); +global char *Mailer init(DEF_MAILER); +global char *ExtBrowser init(DEF_EXT_BROWSER); +global char *ExtBrowser2 init(NULL); +global char *ExtBrowser3 init(NULL); +global int BackgroundExtViewer init(TRUE); +global char *ftppasswd init(NULL); +global int do_download init(FALSE); +global char *UserAgent init(NULL); +global int NoSendReferer init(FALSE); +global char *AcceptLang init(NULL); +global int WrapDefault init(FALSE); +global int IgnoreCase init(TRUE); +global int WrapSearch init(FALSE); +global int squeezeBlankLine init(FALSE); +global char *BookmarkFile init(NULL); +global char *pauth init(NULL); +global Str proxy_auth_cookie init(NULL); +global int UseExternalDirBuffer init(TRUE); +#ifdef __EMX__ +global char *DirBufferCommand init("file:///$LIB/dirlist.cmd"); +#else +global char *DirBufferCommand init("file:///$LIB/dirlist.cgi"); +#endif /* __EMX__ */ +global char *config_file init(NULL); +global int ignore_null_img_alt init(TRUE); + +global struct auth_cookie *Auth_cookie init(NULL); +global char *Local_cookie init(NULL); +#ifdef USE_COOKIE +global struct cookie *First_cookie init(NULL); +#endif /* USE_COOKIE */ + +global struct mailcap **UserMailcap; +global struct table2 **UserMimeTypes; +global TextList *mailcap_list; +global TextList *mimetypes_list; +global char *mailcap_files init(USER_MAILCAP ", " SYS_MAILCAP); +global char *mimetypes_files init(USER_MIMETYPES ", " SYS_MIMETYPES); + +global TextList *fileToDelete; + +extern Hist *LoadHist; +extern Hist *SaveHist; +extern Hist *URLHist; +extern Hist *ShellHist; +extern Hist *TextHist; +#ifdef USE_HISTORY +global int URLHistSize init(100); +global int SaveURLHist init(TRUE); +#endif /* USE_HISTORY */ +global int multicolList init(FALSE); + +#ifdef JP_CHARSET +extern char DisplayCode; +extern char DocumentCode; +#endif /* JP_CHARSET */ +#ifndef KANJI_SYMBOLS +global int no_graphic_char init(FALSE); +extern char alt_rule[]; +#endif /* not KANJI_SYMBOLS */ +global char *rc_dir; +global int rc_dir_is_tmp init(FALSE); + +#ifdef MOUSE +global int use_mouse init(TRUE); +extern int mouseActive; +global int reverse_mouse init(FALSE); +#endif /* MOUSE */ + +#ifdef USE_COOKIE +global int default_use_cookie init(TRUE); +global int use_cookie init(TRUE); +global int accept_cookie init(FALSE); +global int accept_bad_cookie init(FALSE); +global char *cookie_reject_domains init(NULL); +global char *cookie_accept_domains init(NULL); +global TextList *Cookie_reject_domains; +global TextList *Cookie_accept_domains; +#endif /* USE_COOKIE */ + +#ifdef VIEW_UNSEENOBJECTS +global int view_unseenobject init(TRUE); +#endif /* VIEW_UNSEENOBJECTS */ + +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) +global int ssl_verify_server init(FALSE); +global char *ssl_cert_file init(NULL); +global char *ssl_key_file init(NULL); +global char *ssl_ca_path init(NULL); +global char *ssl_ca_file init(NULL); +global int ssl_path_modified init(FALSE); +#endif /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ +#ifdef USE_SSL +global char *ssl_forbid_method init(NULL); +#endif + +global int is_redisplay init(FALSE); +global int clear_buffer init(TRUE); +global double pixel_per_char init(DEFAULT_PIXEL_PER_CHAR); +global int use_lessopen init(FALSE); + +#ifdef JP_CHARSET +#define is_kanji(s) (IS_KANJI1((s)[0])&&IS_KANJI2((s)[1])) +#define get_mctype(s) (is_kanji(s)?PC_KANJI:GET_PCTYPE(*(s))) +#define get_mclen(m) (((m)==PC_KANJI)?2:1) +#define mctowc(s,m) \ + (((m)==PC_KANJI)?((unsigned char)(s)[0]|((unsigned char)(s)[1]<<8)): \ + (unsigned char)(s)[0]) +#define is_wckanji(wc) ((wc)&~0xff) +#define get_wctype(wc) (is~wckanji(wc)?PC_KANJI:GET_PCTYPE(wc)) +#else +#define get_mctype(s) GET_PCTYPE(*(s)) +#define get_mclen(m) 1 +#define mctowc(s,m) ((unsigned char)*(s)) +#define is_wckanji(wc) ((wc)&~0xff) +#define get_wctype(wc) (is~wckanji(wc)?PC_ASCII:GET_PCTYPE(wc)) +#endif + +global int w3m_backend init(FALSE); +global Str backend_halfdump_str; +global TextList *backend_batch_commands init(NULL); +int backend( void ); + +/* + * Externals + */ + +#include "table.h" +#include "proto.h" + +#endif /* not FM_H */ @@ -0,0 +1,590 @@ + +/* $Id: form.c,v 1.1 2001/11/08 05:14:53 a-ito Exp $ */ +/* + * HTML forms + */ +#include "fm.h" +#include "parsetag.h" +#include "parsetagx.h" +#include "myctype.h" +#include "local.h" + +#ifdef __EMX__ +/* lstat is identical to stat, only the link itself is statted, not the + * file that is obtained by tracing the links. But on OS/2 systems, + * there is no differences. */ +#define lstat stat +#endif /* __EMX__ */ + +extern Str textarea_str[]; +#ifdef MENU_SELECT +extern FormSelectOption select_option[]; +#include "menu.h" +#endif /* MENU_SELECT */ + +struct { + char *action; + void (*rout) (struct parsed_tagarg *); +} internal_action[] = { + { + "map", follow_map + }, + { + "option", panel_set_option + }, +#ifdef USE_COOKIE + { + "cookie", set_cookie_flag + }, +#endif /* USE_COOKIE */ + { + NULL, NULL + }, +}; + +struct form_list * +newFormList(char *action, char *method, char *charset, char *enctype, char *target, struct form_list *_next) +{ + struct form_list *l; + Str a = Strnew_charp(action); + int m = FORM_METHOD_GET; + int e = FORM_ENCTYPE_URLENCODED; + int c = 0; + + if (method == NULL || !strcasecmp(method, "get")) + m = FORM_METHOD_GET; + else if (!strcasecmp(method, "post")) + m = FORM_METHOD_POST; + else if (!strcasecmp(method, "internal")) + m = FORM_METHOD_INTERNAL; + /* unknown method is regarded as 'get' */ + + if (enctype != NULL && !strcasecmp(enctype, "multipart/form-data")) { + e = FORM_ENCTYPE_MULTIPART; + if (m == FORM_METHOD_GET) + m = FORM_METHOD_POST; + } + + if (charset != NULL) + c = *charset; + + l = New(struct form_list); + l->item = l->lastitem = NULL; + l->action = a; + l->method = m; + l->charset = c; + l->enctype = e; + l->target = target; + l->next = _next; + l->nitems = 0; + l->body = NULL; + l->length = 0; + return l; +} + +/* + * add <input> element to form_list + */ +struct form_item_list * +formList_addInput(struct form_list *fl, struct parsed_tag *tag) +{ + struct form_item_list *item; + char *p; + int i; + + /* if not in <form>..</form> environment, just ignore <input> tag */ + if (fl == NULL) + return NULL; + + item = New(struct form_item_list); + item->type = FORM_UNKNOWN; + item->size = -1; + item->rows = 0; + item->checked = 0; + item->accept = 0; + item->name = NULL; + item->value = NULL; + if (parsedtag_get_value(tag, ATTR_TYPE, &p)) { + item->type = formtype(p); + if (item->size < 0 && + (item->type == FORM_INPUT_TEXT || + item->type == FORM_INPUT_FILE || + item->type == FORM_INPUT_PASSWORD)) + item->size = FORM_I_TEXT_DEFAULT_SIZE; + } + if (parsedtag_get_value(tag, ATTR_NAME, &p)) + item->name = Strnew_charp(p); + if (parsedtag_get_value(tag, ATTR_VALUE, &p)) + item->value = Strnew_charp(p); + item->checked = parsedtag_exists(tag, ATTR_CHECKED); + item->accept = parsedtag_exists(tag, ATTR_ACCEPT); + parsedtag_get_value(tag, ATTR_SIZE, &item->size); + parsedtag_get_value(tag, ATTR_MAXLENGTH, &item->maxlength); + if (parsedtag_get_value(tag, ATTR_TEXTAREANUMBER, &i)) + item->value = textarea_str[i]; +#ifdef MENU_SELECT + if (parsedtag_get_value(tag, ATTR_SELECTNUMBER, &i)) + item->select_option = select_option[i].first; +#endif /* MENU_SELECT */ + if (parsedtag_get_value(tag, ATTR_ROWS, &p)) + item->rows = atoi(p); + if (item->type == FORM_UNKNOWN) { + /* type attribute is missing. Ignore the tag. */ + return NULL; + } +#ifdef MENU_SELECT + if (item->type == FORM_SELECT) { + item->value = chooseSelectOption(item->select_option, CHOOSE_VALUE); + item->label = chooseSelectOption(item->select_option, CHOOSE_OPTION); + } +#endif /* MENU_SELECT */ + if (item->type == FORM_INPUT_FILE && item->value && item->value->length) { + /* security hole ! */ + return NULL; + } + item->parent = fl; + item->next = NULL; + if (fl->item == NULL) { + fl->item = fl->lastitem = item; + } + else { + fl->lastitem->next = item; + fl->lastitem = item; + } + if (item->type == FORM_INPUT_HIDDEN) + return NULL; + fl->nitems++; + return item; +} + +static char *_formtypetbl[] = +{ + "text", "password", "checkbox", "radio", "submit", + "reset", "hidden", "image", "select", "textarea", "button", "file", 0, +}; + +static char *_formmethodtbl[] = +{ + "GET", "POST", "INTERNAL", "HEAD" +}; + +char * +form2str(FormItemList * fi) +{ + Str tmp; + if (fi->type == FORM_INPUT_SUBMIT || + fi->type == FORM_INPUT_IMAGE || + fi->type == FORM_INPUT_BUTTON) { + tmp = Strnew_charp(_formmethodtbl[fi->parent->method]); + Strcat_char(tmp, ' '); + Strcat(tmp, fi->parent->action); + return tmp->ptr; + } + else + return _formtypetbl[fi->type]; +} + +int +formtype(char *typestr) +{ + int i; + for (i = 0; _formtypetbl[i]; i++) { + if (!strcasecmp(typestr, _formtypetbl[i])) + return i; + } + return FORM_UNKNOWN; +} + +void +form_recheck_radio(FormItemList * fi, void *data, void (*update_hook) (FormItemList *, void *)) +{ + Str tmp; + FormItemList *f2; + + tmp = fi->name; + for (f2 = fi->parent->item; f2 != NULL; f2 = f2->next) { + if (f2 != fi && Strcmp(tmp, f2->name) == 0 && + f2->type == FORM_INPUT_RADIO) { + f2->checked = 0; + if (update_hook != NULL) { + update_hook(f2, data); + } + } + } + fi->checked = 1; + if (update_hook != NULL) { + update_hook(fi, data); + } +} + +void +formResetBuffer(Buffer * buf, AnchorList * formitem) +{ + int i; + Anchor *a; + FormItemList *f1, *f2; + + if (buf == NULL || buf->formitem == NULL || formitem == NULL) + return; + for (i = 0; i < buf->formitem->nanchor && i < formitem->nanchor; i++) { + a = &buf->formitem->anchors[i]; + f1 = (FormItemList *) a->url; + f2 = (FormItemList *) formitem->anchors[i].url; + if (f1->type != f2->type || + strcmp(((f1->name == NULL) ? "" : f1->name->ptr), + ((f2->name == NULL) ? "" : f2->name->ptr))) + break; /* What's happening */ + switch (f1->type) { + case FORM_INPUT_TEXT: + case FORM_INPUT_PASSWORD: + case FORM_INPUT_FILE: + case FORM_TEXTAREA: + f1->value = f2->value; + break; + case FORM_INPUT_CHECKBOX: + case FORM_INPUT_RADIO: + f1->checked = f2->checked; + break; + case FORM_SELECT: +#ifdef MENU_SELECT + f1->select_option = f2->select_option; + f1->label = f2->label; +#endif /* MENU_SELECT */ + break; + default: + continue; + } + formUpdateBuffer(a, buf, f1); + } +} + +void +formUpdateBuffer(Anchor * a, Buffer * buf, FormItemList * form) +{ + int i, j, k; + Buffer save; + char *p; + int spos, epos, c_len; + Lineprop c_type; + + copyBuffer(&save, buf); + gotoLine(buf, a->start.line); + switch (form->type) { + case FORM_TEXTAREA: + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_PASSWORD: + case FORM_INPUT_CHECKBOX: + case FORM_INPUT_RADIO: +#ifdef MENU_SELECT + case FORM_SELECT: +#endif /* MENU_SELECT */ + spos = a->start.pos - 1; + epos = a->end.pos; + break; + default: + spos = a->start.pos; + epos = a->end.pos - 1; + } + switch (form->type) { + case FORM_INPUT_CHECKBOX: + case FORM_INPUT_RADIO: + if (form->checked) + buf->currentLine->lineBuf[spos + 1] = '*'; + else + buf->currentLine->lineBuf[spos + 1] = ' '; + break; + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_PASSWORD: + case FORM_TEXTAREA: +#ifdef MENU_SELECT + case FORM_SELECT: + if (form->type == FORM_SELECT) + p = form->label->ptr; + else +#endif /* MENU_SELECT */ + p = form->value->ptr; + i = spos + 1; + for (j = 0; p[j];) { + if (p[j] == '\r') { + j++; + continue; + } + c_type = get_mctype(&p[j]); + c_len = get_mclen(c_type); + k = i + c_len; + if (k > epos) + break; +#ifdef JP_CHARSET + if (c_type == PC_KANJI && form->type != FORM_INPUT_PASSWORD) { + SetCharType(buf->currentLine->propBuf[i], PC_KANJI1); + SetCharType(buf->currentLine->propBuf[i+1], PC_KANJI2); + } + else +#endif /* JP_CHARSET */ + SetCharType(buf->currentLine->propBuf[i], PC_ASCII); + + for (; i < k; i++, j++) { + if (form->type == FORM_INPUT_PASSWORD) + buf->currentLine->lineBuf[i] = '*'; + else if (c_type == PC_CTRL || + IS_UNPRINTABLE_ASCII(p[j], c_type)) + buf->currentLine->lineBuf[i] = ' '; + else + buf->currentLine->lineBuf[i] = p[j]; + } + } + for (; i < epos; i++) { + buf->currentLine->lineBuf[i] = ' '; + SetCharType(buf->currentLine->propBuf[i], PC_ASCII); + } + i--; + break; + } + copyBuffer(buf, &save); +} + + +Str +textfieldrep(Str s, int width) +{ + Lineprop c_type; + Str n = Strnew_size(width + 2); + int i, j, k, c_len; + + j = 0; + for (i = 0; i < s->length; i += c_len) { + c_type = get_mctype(&s->ptr[i]); + c_len = get_mclen(c_type); + if (s->ptr[i] == '\r') { + continue; + } + k = j + c_len; + if (k > width) + break; + if (IS_CNTRL(s->ptr[i])) { + Strcat_char(n, ' '); + } + else if (s->ptr[i] == '&') + Strcat_charp(n, "&"); + else if (s->ptr[i] == '<') + Strcat_charp(n, "<"); + else if (s->ptr[i] == '>') + Strcat_charp(n, ">"); + else + Strcat_charp_n(n, &s->ptr[i], c_len); + j = k; + } + for (; j < width; j++) + Strcat_char(n, ' '); + return n; +} + +static void +form_fputs_decode(Str s, FILE * f) +{ + char *p; + Str z = Strnew(); + + for (p = s->ptr; *p;) { + switch (*p) { + case '<': + if (!strncasecmp(p, "<eol>", 5)) { + Strcat_char(z, '\n'); + p += 5; + } + else { + Strcat_char(z, *p); + p++; + } + break; +#if !defined(CYGWIN) && !defined(__EMX__) + case '\r': + if (*(p + 1) == '\n') + p++; + /* continue to the next label */ +#endif /* !defined(CYGWIN) && !defined(__EMX__) */ + default: + Strcat_char(z, *p); + p++; + break; + } + } +#ifdef JP_CHARSET + fputs(conv_str(z, InnerCode, DisplayCode)->ptr, f); +#else /* not JP_CHARSET */ + fputs(z->ptr, f); +#endif /* not JP_CHARSET */ +} + + +void +input_textarea(FormItemList * fi) +{ + Str tmpname = tmpfname(TMPF_DFL, NULL); + Str tmp; + FILE *f; + + f = fopen(tmpname->ptr, "w"); + if (f == NULL) { + disp_err_message("Can't open temporary file", FALSE); + return; + } + if (fi->value) + form_fputs_decode(fi->value, f); + fclose(f); + if (strcasestr(Editor, "%s")) + tmp = Sprintf(Editor, tmpname->ptr); + else + tmp = Sprintf("%s %s", Editor, tmpname->ptr); + fmTerm(); + system(tmp->ptr); + fmInit(); + + f = fopen(tmpname->ptr, "r"); + if (f == NULL) { + disp_err_message("Can't open temporary file", FALSE); + return; + } + fi->value = Strnew(); + while (tmp = Strfgets(f), tmp->length > 0) { + if (tmp->length == 1 && tmp->ptr[tmp->length - 1] == '\n') { + /* null line with bare LF */ + tmp = Strnew_charp("\r\n"); + } + else if (tmp->length > 1 && tmp->ptr[tmp->length - 1] == '\n' && + tmp->ptr[tmp->length - 2] != '\r') { + Strshrink(tmp, 1); + Strcat_charp(tmp, "\r\n"); + } +#ifdef JP_CHARSET + Strcat(fi->value, conv_str(tmp, DisplayCode, InnerCode)); +#else /* not JP_CHARSET */ + Strcat(fi->value, tmp); +#endif /* not JP_CHARSET */ + } + fclose(f); + unlink(tmpname->ptr); +} + +void +do_internal(char *action, char *data) +{ + int i; + + for (i = 0; internal_action[i].action; i++) { + if (strcasecmp(internal_action[i].action, action) == 0) { + internal_action[i].rout(cgistr2tagarg(data)); + return; + } + } +} + +#ifdef MENU_SELECT +void +addSelectOption(FormSelectOption * fso, Str value, Str label, int chk) +{ + FormSelectOptionItem *o; + o = New(FormSelectOptionItem); + if (value == NULL) + value = label; + o->value = value; + Strremovefirstspaces(label); + Strremovetrailingspaces(label); + o->label = label; + o->checked = chk; + o->next = NULL; + if (fso->first == NULL) + fso->first = fso->last = o; + else { + fso->last->next = o; + fso->last = o; + } +} + +Str +chooseSelectOption(FormSelectOptionItem * item, int choose_type) +{ + Str chosen; + if (item == NULL) + return Strnew_size(0); + if (choose_type == CHOOSE_OPTION) + chosen = item->label; + else + chosen = item->value; + while (item) { + if (item->checked) { + if (choose_type == CHOOSE_OPTION) + chosen = item->label; + else + chosen = item->value; + } + item = item->next; + } + return chosen; +} + +void +formChooseOptionByMenu(struct form_item_list *fi, int x, int y) +{ + int i, n, selected = -1, init_select = 0; + FormSelectOptionItem *opt; + char **label; + + for (n = 0, opt = fi->select_option; opt != NULL; n++, opt = opt->next); + label = New_N(char *, n + 1); + for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) { + label[i] = opt->label->ptr; + if (!Strcmp(fi->value, opt->value)) + init_select = i; + } + label[n] = NULL; + + optionMenu(x, y, label, &selected, init_select, NULL); + + for (i = 0, opt = fi->select_option; opt != NULL; i++, opt = opt->next) { + if (i == selected) { + fi->value = opt->value; + fi->label = opt->label; + break; + } + } +} +#endif /* MENU_SELECT */ + +void +form_write_data(FILE * f, char *boundary, char *name, char *value) +{ + fprintf(f, "--%s\r\n", boundary); + fprintf(f, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", name); + fprintf(f, "%s\r\n", value); +} + +void +form_write_form_file(FILE * f, char *boundary, char *name, char *file) +{ + FILE *fd; + struct stat st; + int c; + + fprintf(f, "--%s\r\n", boundary); + fprintf(f, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", + name, mybasename(file)); + fprintf(f, "Content-Type: text/plain\r\n\r\n"); + +#ifdef READLINK + if (lstat(file, &st) < 0) + goto write_end; +#endif /* READLINK */ + if (S_ISDIR(st.st_mode)) + goto write_end; + fd = fopen(file, "r"); + if (fd != NULL) { + while ((c = fgetc(fd)) != EOF) + fputc(c, f); + fclose(fd); + } + write_end: + fprintf(f, "\r\n"); +} @@ -0,0 +1,96 @@ +/* + * HTML forms + */ +#ifndef FORM_H +#define FORM_H + +#include "Str.h" + +#define FORM_UNKNOWN -1 +#define FORM_INPUT_TEXT 0 +#define FORM_INPUT_PASSWORD 1 +#define FORM_INPUT_CHECKBOX 2 +#define FORM_INPUT_RADIO 3 +#define FORM_INPUT_SUBMIT 4 +#define FORM_INPUT_RESET 5 +#define FORM_INPUT_HIDDEN 6 +#define FORM_INPUT_IMAGE 7 +#define FORM_SELECT 8 +#define FORM_TEXTAREA 9 +#define FORM_INPUT_BUTTON 10 +#define FORM_INPUT_FILE 11 + +#define FORM_I_TEXT_DEFAULT_SIZE 40 +#define FORM_I_SELECT_DEFAULT_SIZE 40 +#define FORM_I_TEXTAREA_DEFAULT_WIDTH 40 + +#define FORM_METHOD_GET 0 +#define FORM_METHOD_POST 1 +#define FORM_METHOD_INTERNAL 2 +#define FORM_METHOD_HEAD 3 + +#define FORM_ENCTYPE_URLENCODED 0 +#define FORM_ENCTYPE_MULTIPART 1 + +#define MAX_TEXTAREA 100 /* max number of * <textarea>..</textarea> + * within one * document */ +#ifdef MENU_SELECT +#define MAX_SELECT 100 /* max number of <select>..</select> * + * within one document */ +#endif /* MENU_SELECT */ + +typedef struct form_list { + struct form_item_list *item; + struct form_item_list *lastitem; + int method; + Str action; + char *target; + int charset; + int enctype; + struct form_list *next; + int nitems; + char *body; + char *boundary; + unsigned long length; +} FormList; + +#ifdef MENU_SELECT +typedef struct form_select_option_item { + Str value; + Str label; + int checked; + struct form_select_option_item *next; +} FormSelectOptionItem; + +typedef struct form_select_option { + FormSelectOptionItem *first; + FormSelectOptionItem *last; +} FormSelectOption; + +void addSelectOption(FormSelectOption * fso, Str value, Str label, int chk); +Str chooseSelectOption(FormSelectOptionItem * item, int choose_type); +void formChooseOptionByMenu(struct form_item_list *fi, int x, int y); +/* macros for chooseSelectOption */ +#define CHOOSE_OPTION 0 +#define CHOOSE_VALUE 1 +#endif /* MENU_SELECT */ + +typedef struct form_item_list { + int type; + Str name; + Str value; + int checked; + int accept; + int size; + int rows; + int maxlength; +#ifdef MENU_SELECT + FormSelectOptionItem *select_option; + Str label; +#endif /* MENU_SELECT */ + struct form_list *parent; + struct form_item_list *next; + int anchor_num; +} FormItemList; + +#endif /* not FORM_H */ @@ -0,0 +1,789 @@ +/* $Id: frame.c,v 1.1 2001/11/08 05:14:54 a-ito Exp $ */ +#include "fm.h" +#include "parsetagx.h" +#include "myctype.h" +#include <signal.h> +#include <setjmp.h> + +#ifdef KANJI_SYMBOLS +#define RULE_WIDTH 2 +#else /* not KANJI_SYMBOLS */ +#define RULE_WIDTH 1 +#endif /* not KANJI_SYMBOLS */ + +static JMP_BUF AbortLoading; +struct frameset *renderFrameSet = NULL; + +static MySignalHandler +KeyAbort(SIGNAL_ARG) +{ + LONGJMP(AbortLoading, 1); +} + +struct frameset * +newFrameSet(struct parsed_tag *tag) +{ + struct frameset *f; + int i; + char *cols = NULL, *rows = NULL, *p, *q; + char *length[100]; + + f = New(struct frameset); + f->attr = F_FRAMESET; + f->name = NULL; + f->currentURL = NULL; + parsedtag_get_value(tag, ATTR_COLS, &cols); + parsedtag_get_value(tag, ATTR_ROWS, &rows); + i = 0; + if (cols) { + length[i] = p = cols; + while (*p != '\0') + if (*p++ == ',') + length[++i] = p; + length[++i] = p + 1; + } + if (i > 1) { + f->col = i; + f->width = New_N(char *, i); + p = length[i]; + do { + i--; + q = p - 2; + p = length[i]; + switch (*q) { + Str tmp; + case '%': + f->width[i] = allocStr(p, q - p + 1); + break; + case '*': + f->width[i] = "*"; + break; + default: + tmp = Sprintf("%d", atoi(p)); + f->width[i] = tmp->ptr; + break; + } + } while (i); + } + else { + f->col = 1; + f->width = New_N(char *, 1); + f->width[0] = "*"; + } + i = 0; + if (rows) { + length[i] = p = rows; + while (*p != '\0') + if (*p++ == ',') + length[++i] = p; + length[++i] = p + 1; + } + if (i > 1) { + f->row = i; + f->height = New_N(char *, i); + p = length[i]; + do { + i--; + q = p - 2; + p = length[i]; + f->height[i] = allocStr(p, q - p + 1); + } while (i); + } + else { + f->row = 1; + f->height = New_N(char *, 1); + f->height[0] = "*"; + } + i = f->row * f->col; + f->i = 0; + f->frame = New_N(union frameset_element, i); + do { + i--; + f->frame[i].element = NULL; + } while (i); + return f; +} + +struct frame_body * +newFrame(struct parsed_tag *tag, ParsedURL * baseURL) +{ + struct frame_body *body; + char *p; + + body = New(struct frame_body); + bzero((void *) body, sizeof(*body)); + body->attr = F_UNLOADED; + body->flags = 0; + body->baseURL = baseURL; + if (tag) { + parsedtag_get_value(tag, ATTR_SRC, &body->url); + if (parsedtag_get_value(tag, ATTR_NAME, &p) && *p != '_') + body->name = p; + } + return body; +} + +static void +unloadFrame(struct frame_body *b) +{ + if (b->source && b->flags & FB_TODELETE) + pushText(fileToDelete, b->source); + b->attr = F_UNLOADED; +} + +void +deleteFrame(struct frame_body *b) +{ + if (b == NULL) + return; + unloadFrame(b); + bzero((void *) b, sizeof(*b)); +} + +void +addFrameSetElement(struct frameset *f, union frameset_element element) +{ + int i; + + if (f == NULL) + return; + i = f->i; + if (i >= f->col * f->row) + return; + f->frame[i] = element; + f->i++; +} + +void +deleteFrameSet(struct frameset *f) +{ + int i; + + if (f == NULL) + return; + for (i = 0; i < f->col * f->row; i++) { + deleteFrameSetElement(f->frame[i]); + } + f->name = NULL; + f->currentURL = NULL; + return; +} + +void +deleteFrameSetElement(union frameset_element e) +{ + if (e.element == NULL) + return; + switch (e.element->attr) { + case F_UNLOADED: + break; + case F_BODY: + deleteFrame(e.body); + break; + case F_FRAMESET: + deleteFrameSet(e.set); + break; + default: + break; + } + return; +} + +static struct frame_body * +copyFrame(struct frame_body *ob) +{ + struct frame_body *rb; + + rb = New(struct frame_body); + bcopy((const void *) ob, (void *) rb, sizeof(struct frame_body)); + rb->flags &= ~FB_TODELETE; + return rb; +} + +struct frameset * +copyFrameSet(struct frameset *of) +{ + struct frameset *rf; + int n; + + rf = New(struct frameset); + n = of->col * of->row; + bcopy((const void *) of, (void *) rf, sizeof(struct frameset)); + rf->width = New_N(char *, rf->col); + bcopy((const void *) of->width, + (void *) rf->width, + sizeof(char *) * rf->col); + rf->height = New_N(char *, rf->row); + bcopy((const void *) of->height, + (void *) rf->height, + sizeof(char *) * rf->row); + rf->frame = New_N(union frameset_element, n); + while (n) { + n--; + if (!of->frame[n].element) + goto attr_default; + switch (of->frame[n].element->attr) { + case F_UNLOADED: + case F_BODY: + rf->frame[n].body = copyFrame(of->frame[n].body); + break; + case F_FRAMESET: + rf->frame[n].set = copyFrameSet(of->frame[n].set); + break; + default: + attr_default: + rf->frame[n].element = NULL; + break; + } + } + return rf; +} + +void +flushFrameSet(struct frameset *fs) +{ + int n = fs->i; + + while (n) { + n--; + if (!fs->frame[n].element) + goto attr_default; + switch (fs->frame[n].element->attr) { + case F_UNLOADED: + case F_BODY: + fs->frame[n].body->nameList = NULL; + break; + case F_FRAMESET: + flushFrameSet(fs->frame[n].set); + break; + default: + attr_default: + /* nothing to do */ + break; + } + } +} + +void +pushFrameTree(struct frameset_queue **fqpp, + struct frameset *fs, + long linenumber, + short pos) +{ + struct frameset_queue *rfq, *cfq = *fqpp; + + if (!fs) + return; + + rfq = New(struct frameset_queue); + rfq->linenumber = linenumber; + rfq->pos = pos; + + rfq->back = cfq; + if (cfq) { + rfq->next = cfq->next; + if (cfq->next) + cfq->next->back = rfq; + cfq->next = rfq; + } + else + rfq->next = cfq; + rfq->frameset = fs; + *fqpp = rfq; + return; +} + +struct frameset * +popFrameTree(struct frameset_queue **fqpp, + long *linenumber, + short *pos) +{ + struct frameset_queue *rfq = NULL, *cfq = *fqpp; + struct frameset *rfs = NULL; + + if (!cfq) + return rfs; + + if (linenumber) + *linenumber = cfq->linenumber; + if (pos) + *pos = cfq->pos; + + rfs = cfq->frameset; + if (cfq->next) { + (rfq = cfq->next)->back = cfq->back; + } + if (cfq->back) { + (rfq = cfq->back)->next = cfq->next; + } + *fqpp = rfq; + bzero((void *) cfq, sizeof(struct frameset_queue)); + return rfs; +} + +void +resetFrameElement(union frameset_element *f_element, + Buffer * buf, + char *referer, + FormList * request) +{ + char *f_name; + struct frame_body *f_body; + + f_name = f_element->element->name; + if (buf->frameset) { + /* frame cascade */ + deleteFrameSetElement(*f_element); + f_element->set = buf->frameset; + f_element->set->currentURL = New(ParsedURL); + copyParsedURL(f_element->set->currentURL, &buf->currentURL); + buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL); + f_element->set->name = f_name; + } + else { + f_body = newFrame(NULL, baseURL(buf)); + f_body->attr = F_BODY; + f_body->name = f_name; + f_body->url = parsedURL2Str(&buf->currentURL)->ptr; + if (buf->real_scheme == SCM_LOCAL) { + f_body->source = buf->sourcefile; + } + else { + Str tmp = tmpfname(TMPF_FRAME, NULL); + rename(buf->sourcefile, tmp->ptr); + f_body->source = tmp->ptr; + f_body->flags |= FB_TODELETE; + buf->sourcefile = NULL; + } + f_body->referer = referer; + f_body->request = request; + deleteFrameSetElement(*f_element); + f_element->body = f_body; + } +} + +static struct frameset * +frame_download_source(struct frame_body *b, ParsedURL * currentURL, + ParsedURL * baseURL, int flag) +{ + Buffer *buf; + struct frameset *ret_frameset = NULL; + Str tmp; + ParsedURL url; + + if (b == NULL || b->url == NULL || b->url[0] == '\0') + return NULL; + if (b->baseURL) + baseURL = b->baseURL; + parseURL2(b->url, &url, currentURL); + switch (url.scheme) { + case SCM_LOCAL: + b->source = url.file; + b->flags = 0; + default: + is_redisplay = TRUE; + buf = loadGeneralFile(b->url, + baseURL ? baseURL : currentURL, + b->referer, + flag, + b->request); + is_redisplay = FALSE; + break; + } + + if (buf == NULL || buf == NO_BUFFER) { + b->source = NULL; + b->flags = (buf == NO_BUFFER) ? FB_NO_BUFFER : 0; + return NULL; + } + b->url = parsedURL2Str(&buf->currentURL)->ptr; + if (buf->real_scheme != SCM_LOCAL) { + tmp = tmpfname(TMPF_FRAME, NULL); + rename(buf->sourcefile, tmp->ptr); + b->source = tmp->ptr; + b->flags |= FB_TODELETE; + buf->sourcefile = NULL; + } + b->attr = F_BODY; + if (buf->frameset) { + ret_frameset = buf->frameset; + ret_frameset->name = b->name; + ret_frameset->currentURL = New(ParsedURL); + copyParsedURL(ret_frameset->currentURL, &buf->currentURL); + buf->frameset = popFrameTree(&(buf->frameQ), NULL, NULL); + } + discardBuffer(buf); + return ret_frameset; +} + +static int +createFrameFile(struct frameset *f, FILE * f1, Buffer * current, int level, int force_reload) +{ + int r, c, t_stack; + InputStream f2; +#ifdef JP_CHARSET + char code, ic, charset[2]; +#endif /* JP_CHARSET */ + char *d_target, *p_target, *s_target, *t_target; + ParsedURL *currentURL, base; + MySignalHandler(*prevtrap) (SIGNAL_ARG) = NULL; + int flag; + + if (f == NULL) + return -1; + + if (level == 0) { + if (SETJMP(AbortLoading) != 0) { + if (fmInitialized) + term_raw(); + signal(SIGINT, prevtrap); + return -1; + } + prevtrap = signal(SIGINT, KeyAbort); + if (fmInitialized) + term_cbreak(); + + f->name = "_top"; + } + + if (level > 7) { + fputs("Too many frameset tasked.\n", f1); + return -1; + } + + if (level == 0) { + fprintf(f1, "<html><head><title>%s</title></head><body>\n", + htmlquote_str(current->buffername)); + fputs("<table hborder width=\"100%\">\n", f1); + } + else + fputs("<table hborder>\n", f1); + + currentURL = f->currentURL ? f->currentURL : ¤t->currentURL; +#ifdef JP_CHARSET + charset[1] = '\0'; +#endif + for (r = 0; r < f->row; r++) { + fputs("<tr valign=top>\n", f1); + for (c = 0; c < f->col; c++) { + union frameset_element frame; + struct frameset *f_frameset; + int i = c + r * f->col; + char *p = ""; + Str tok = Strnew(); + int status; + + frame = f->frame[i]; + + if (frame.element == NULL) { + fputs("<td>\n</td>\n", f1); + continue; + } + + fputs("<td", f1); + if (frame.element->name) + fprintf(f1, " id=\"_%s\"", htmlquote_str(frame.element->name)); + if (!r) + fprintf(f1, " width=\"%s\"", f->width[c]); + fputs(">\n", f1); + + flag = 0; + if (force_reload) { + flag |= RG_NOCACHE; + if (frame.element->attr == F_BODY) + unloadFrame(frame.body); + } + switch (frame.element->attr) { + default: + fprintf(f1, "Frameset \"%s\" frame %d: type unrecognized", + htmlquote_str(f->name), i + 1); + break; + case F_UNLOADED: + if (!frame.body->name && f->name) { + frame.body->name = Sprintf("%s_%d", f->name, i)->ptr; + } + f_frameset = frame_download_source(frame.body, + currentURL, + current->baseURL, + flag); + if (f_frameset) { + deleteFrame(frame.body); + f->frame[i].set = frame.set = f_frameset; + goto render_frameset; + } + /* fall through */ + case F_BODY: + f2 = NULL; + if (frame.body->source == NULL || + (f2 = openIS(frame.body->source)) == NULL) { + frame.body->attr = F_UNLOADED; + if (frame.body->flags & FB_NO_BUFFER) + fprintf(f1, "Open %s with other method", frame.body->url); + else + fprintf(f1, "Can't open %s", frame.body->url); + break; + } + parseURL2(frame.body->url, &base, currentURL); + p_target = f->name; + s_target = frame.body->name; + t_target = "_blank"; + d_target = TargetSelf ? s_target : t_target; +#ifdef JP_CHARSET + code = '\0'; +#endif /* JP_CHARSET */ + t_stack = 0; + do { + status = R_ST_NORMAL; + do { + if (*p == '\0') { + Str tmp = StrmyISgets(f2); + if (tmp->length == 0 && status != R_ST_NORMAL) + tmp = correct_irrtag(status); + if (tmp->length == 0) + break; +#ifdef JP_CHARSET + if ((ic = checkShiftCode(tmp, code)) != '\0') + tmp = conv_str(tmp, (code = ic), InnerCode); + +#endif /* JP_CHARSET */ + cleanup_line(tmp, HTML_MODE); + p = tmp->ptr; + } + if (status == R_ST_NORMAL || ST_IS_COMMENT(status)) + read_token(tok, &p, &status, 1, 0); + else + read_token(tok, &p, &status, 1, 1); + } while (status != R_ST_NORMAL); + + if (tok->length == 0) + continue; + + if (tok->ptr[0] == '<') { + char *q = tok->ptr; + int j, a_target = 0; + struct parsed_tag *tag; + ParsedURL url; + + if (!(tag = parse_tag(&q, FALSE))) + goto token_end; + + switch (tag->tagid) { + case HTML_TITLE: + fputs("<!-- title:", f1); + goto token_end; + case HTML_N_TITLE: + fputs("-->", f1); + goto token_end; + case HTML_BASE: + if (parsedtag_get_value(tag, ATTR_HREF, &q)) + parseURL(q, &base, NULL); + if (parsedtag_get_value(tag, ATTR_TARGET, &q)) { + if (!strcasecmp(q, "_self")) + d_target = s_target; + else if (!strcasecmp(q, "_parent")) + d_target = p_target; + else + d_target = q; + } + /* fall thru, "BASE" is prohibit tag */ + case HTML_HEAD: + case HTML_N_HEAD: + case HTML_BODY: + case HTML_N_BODY: + case HTML_META: + case HTML_DOCTYPE: + /* prohibit_tags */ + Strshrinkfirst(tok, 1); + Strshrink(tok, 1); + fprintf(f1, "<!-- %s -->", tok->ptr); + goto token_end; + case HTML_TABLE: + t_stack++; + break; + case HTML_N_TABLE: + t_stack--; + if (t_stack < 0) { + t_stack = 0; + Strshrinkfirst(tok, 1); + Strshrink(tok, 1); + fprintf(f1, + "<!-- table stack underflow: %s -->", + tok->ptr); + goto token_end; + } + break; + case HTML_THEAD: + case HTML_N_THEAD: + case HTML_TBODY: + case HTML_N_TBODY: + case HTML_TFOOT: + case HTML_N_TFOOT: + case HTML_TD: + case HTML_N_TD: + case HTML_TR: + case HTML_N_TR: + case HTML_TH: + case HTML_N_TH: + /* table_tags MUST be in table stack */ + if (!t_stack) { + Strshrinkfirst(tok, 1); + Strshrink(tok, 1); + fprintf(f1, "<!-- %s -->", tok->ptr); + goto token_end; + + } + break; + default: + break; + } + for (j = 0; j < TagMAP[tag->tagid].max_attribute; j++) { + switch (tag->attrid[j]) { + case ATTR_SRC: + case ATTR_HREF: + case ATTR_ACTION: + if (!tag->value[j]) + break; + parseURL2(tag->value[j], &url, &base); + if (url.scheme == SCM_MAILTO || + url.scheme == SCM_UNKNOWN || + url.scheme == SCM_MISSING) + break; + a_target |= 1; + tag->value[j] = parsedURL2Str(&url)->ptr; + tag->need_reconstruct = TRUE; + parsedtag_set_value(tag, + ATTR_REFERER, + parsedURL2Str(&base)->ptr); +#ifdef JP_CHARSET + if (code != '\0') { + charset[0] = code; + parsedtag_set_value(tag, + ATTR_CHARSET, + charset); + } +#endif + break; + case ATTR_TARGET: + if (!tag->value[j]) + break; + a_target |= 2; + if (!strcasecmp(tag->value[j], "_self")) { + parsedtag_set_value(tag, + ATTR_TARGET, + s_target); + } + else if (!strcasecmp(tag->value[j], + "_parent")) { + parsedtag_set_value(tag, + ATTR_TARGET, + p_target); + } + break; + case ATTR_NAME: + case ATTR_ID: + if (!tag->value[j]) + break; + parsedtag_set_value(tag, + ATTR_FRAMENAME, + s_target); + break; + } + } + if (a_target == 1) { + /* there is HREF attribute and no TARGET + * attribute */ + parsedtag_set_value(tag, + ATTR_TARGET, + d_target); + } + if (parsedtag_need_reconstruct(tag)) + Strfputs(parsedtag2str(tag), f1); + else + Strfputs(tok, f1); + } + else { + Strfputs(tok, f1); + } + token_end: + Strclear(tok); + } while (*p != '\0' || !iseos(f2)); + while (t_stack--) + fputs("</TABLE>\n", f1); + ISclose(f2); + break; + case F_FRAMESET: + render_frameset: + if (!frame.set->name && f->name) { + frame.set->name = Sprintf("%s_%d", f->name, i)->ptr; + } + createFrameFile(frame.set, f1, current, level + 1, force_reload); + break; + } + fputs("</td>\n", f1); + } + fputs("</tr>\n", f1); + } + + fputs("</table>\n", f1); + if (level == 0) { + fputs("</body></html>\n", f1); + signal(SIGINT, prevtrap); + if (fmInitialized) + term_raw(); + } + return 0; +} + +Buffer * +renderFrame(Buffer * Cbuf, int force_reload) +{ + Str tmp; + FILE *f; + Buffer *buf; + int flag; + struct frameset *fset; + + tmp = tmpfname(TMPF_FRAME, ".html"); + f = fopen(tmp->ptr, "w"); + if (f == NULL) + return NULL; + /* + * if (Cbuf->frameQ != NULL) fset = Cbuf->frameQ->frameset; else */ + fset = Cbuf->frameset; + if (fset == NULL || createFrameFile(fset, f, Cbuf, 0, force_reload) < 0) + return NULL; + fclose(f); + flag = RG_FRAME; + if ((Cbuf->currentURL).is_nocache) + flag |= RG_NOCACHE; + renderFrameSet = Cbuf->frameset; + flushFrameSet(renderFrameSet); + buf = loadGeneralFile(tmp->ptr, NULL, NULL, flag, NULL); + renderFrameSet = NULL; + if (buf == NULL || buf == NO_BUFFER) + return NULL; + buf->sourcefile = tmp->ptr; + copyParsedURL(&buf->currentURL, &Cbuf->currentURL); + return buf; +} + +union frameset_element * +search_frame(struct frameset *fset, char *name) +{ + int i; + union frameset_element *e = NULL; + + for (i = 0; i < fset->col * fset->row; i++) { + e = &(fset->frame[i]); + if (e->element != NULL) { + if (e->element->name && !strcmp(e->element->name, name)) { + return e; + } + else if (e->element->attr == F_FRAMESET && + (e = search_frame(e->set, name))) { + return e; + } + } + } + return NULL; +} @@ -0,0 +1,56 @@ +/* + * frame support + */ + +struct frame_element { + char attr; +#define F_UNLOADED 0x00 +#define F_BODY 0x01 +#define F_FRAMESET 0x02 + char dummy; + char *name; +}; + +struct frame_body { + char attr; + char flags; +#define FB_NOCACHE 0x01 +#define FB_TODELETE 0x02 +#define FB_NO_BUFFER 0x04 + char *name; + char *url; + ParsedURL *baseURL; + char *source; + char *referer; + struct _anchorList *nameList; + FormList *request; +}; + +union frameset_element { + struct frame_element *element; + struct frame_body *body; + struct frameset *set; +}; + +struct frameset { + char attr; + char dummy; + char *name; + ParsedURL *currentURL; + char **width; + char **height; + int col; + int row; + int i; + union frameset_element *frame; +}; + +struct frameset_queue { + struct frameset_queue *next; + struct frameset_queue *back; + struct frameset *frameset; + long linenumber; + short pos; +}; + +extern struct frameset *renderFrameSet; @@ -0,0 +1,871 @@ +#include <stdio.h> +#include <pwd.h> +#include <Str.h> +#include <ctype.h> +#include <signal.h> +#include <setjmp.h> + +#include "fm.h" +#include "html.h" +#include "myctype.h" + +#ifdef DEBUG +#include <malloc.h> +#endif /* DEBUG */ + +#include <sys/socket.h> +#if defined(FTPPASS_HOSTNAMEGEN) || defined(INET6) +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#endif + +typedef struct _FTP { + FILE *rcontrol; + FILE *wcontrol; + FILE *data; +} *FTP; + +#define FtpError(status) ((status)<0) +#define FTPDATA(ftp) ((ftp)->data) + +typedef int STATUS; + +static FTP ftp; + +static Str +read_response1(FTP ftp) +{ + char c; + Str buf = Strnew(); + while (1) { + c = getc(ftp->rcontrol); + if (c == '\r') { + c = getc(ftp->rcontrol); + if (c == '\n') { + Strcat_charp(buf, "\r\n"); + break; + } + else { + Strcat_char(buf, '\r'); + Strcat_char(buf, c); + } + } + else if (c == '\n') { + Strcat_charp(buf, "\r\n"); + break; + } + else if (feof(ftp->rcontrol)) + break; + else + Strcat_char(buf, c); + } + return buf; +} + +Str +read_response(FTP ftp) +{ + Str tmp; + + tmp = read_response1(ftp); + if (feof(ftp->rcontrol)) { + return tmp; + } + if (tmp->ptr[3] == '-') { + /* RFC959 4.2 FTP REPLIES */ + /* multi-line response start */ + /* + * Thus the format for multi-line replies is that the + * first line will begin with the exact required reply + * code, followed immediately by a Hyphen, "-" (also known + * as Minus), followed by text. The last line will begin + * with the same code, followed immediately by Space <SP>, + * optionally some text, and the Telnet end-of-line code. */ + while (1) { + tmp = read_response1(ftp); + if (feof(ftp->rcontrol)) { + break; + } + if (IS_DIGIT(tmp->ptr[0]) + && IS_DIGIT(tmp->ptr[1]) + && IS_DIGIT(tmp->ptr[2]) + && tmp->ptr[3] == ' ') { + break; + } + } + } + return tmp; +} + +int +FtpLogin(FTP * ftp_return, char *host, char *user, char *pass) +{ + Str tmp; + FTP ftp = New(struct _FTP); + int fd; + *ftp_return = ftp; + fd = openSocket(host, "ftp", 21); + if (fd < 0) + return -1; +#ifdef FTPPASS_HOSTNAMEGEN + if (!strcmp(user, "anonymous")) { + size_t n = strlen(pass); + + if (n > 0 && pass[n - 1] == '@') { + struct sockaddr_in sockname; + int socknamelen = sizeof(sockname); + + if (!getsockname(fd, (struct sockaddr *) &sockname, &socknamelen)) { + struct hostent *sockent; + Str tmp2 = Strnew_charp(pass); + + if (sockent = gethostbyaddr((char *) &sockname.sin_addr, + sizeof(sockname.sin_addr), + sockname.sin_family)) + Strcat_charp(tmp2, sockent->h_name); + else + Strcat_m_charp(tmp2, "[", inet_ntoa(sockname.sin_addr), "]", NULL); + + pass = tmp2->ptr; + } + } + } +#endif + ftp->rcontrol = fdopen(fd, "rb"); + ftp->wcontrol = fdopen(dup(fd), "wb"); + ftp->data = NULL; + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 220) + return -1; + if (fmInitialized) { + message(Sprintf("Sending FTP username (%s) to remote server.\n", user)->ptr, 0, 0); + refresh(); + } + tmp = Sprintf("USER %s\r\n", user); + fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + /* + * Some ftp daemons(e.g. publicfile) return code 230 for user command. + */ + if (atoi(tmp->ptr) == 230) + goto succeed; + if (atoi(tmp->ptr) != 331) + return -1; + if (fmInitialized) { + message(Sprintf("Sending FTP password to remote server.\n")->ptr, 0, 0); + refresh(); + } + tmp = Sprintf("PASS %s\r\n", pass); + fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 230) + return -1; + succeed: + return 0; +} + +int +FtpBinary(FTP ftp) +{ + Str tmp; + fwrite("TYPE I\r\n", 8, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 200) + return -1; + return 0; +} + +int +ftp_pasv(FTP ftp) +{ + int n1, n2, n3, n4, p1, p2; + int data_s; + char *p; + Str tmp; + int family; +#ifdef INET6 + struct sockaddr_storage sin; + int sinlen, port; + unsigned char d1, d2, d3, d4; + char abuf[INET6_ADDRSTRLEN]; +#endif + +#ifdef INET6 + sinlen = sizeof(sin); + if (getpeername(fileno(ftp->wcontrol), + (struct sockaddr *)&sin, &sinlen) < 0) + return -1; + family = sin.ss_family; +#else + family = AF_INET; +#endif + switch (family) { +#ifdef INET6 + case AF_INET6: + fwrite("EPSV\r\n", 6, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 229) + return -1; + for (p = tmp->ptr + 4; *p && *p != '('; p++); + if (*p == '\0') + return -1; + if (sscanf(++p, "%c%c%c%d%c", &d1, &d2, &d3, &port, &d4) != 5 + || d1 != d2 || d1 != d3 || d1 != d4) + return -1; + if (getnameinfo((struct sockaddr *)&sin, sinlen, + abuf, sizeof(abuf), + NULL, 0, NI_NUMERICHOST) != 0) + return -1; + tmp = Sprintf("%s", abuf); + data_s = openSocket(tmp->ptr, "", port); + break; +#endif + case AF_INET: + fwrite("PASV\r\n", 6, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 227) + return -1; + for (p = tmp->ptr + 4; *p && !IS_DIGIT(*p); p++); + if (*p == '\0') + return -1; + sscanf(p, "%d,%d,%d,%d,%d,%d", &n1, &n2, &n3, &n4, &p1, &p2); + tmp = Sprintf("%d.%d.%d.%d", n1, n2, n3, n4); + data_s = openSocket(tmp->ptr, "", p1 * 256 + p2); + break; + default: + return -1; + } + if (data_s < 0) + return -1; + ftp->data = fdopen(data_s, "rb"); + return 0; +} + +int +FtpCwd(FTP ftp, char *path) +{ + Str tmp; + + tmp = Sprintf("CWD %s\r\n", path); + fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (tmp->ptr[0] == '5') { + return -1; + } + return 0; +} + +int +FtpOpenRead(FTP ftp, char *path) +{ + Str tmp; + + if (ftp_pasv(ftp) < 0) + return -1; + tmp = Sprintf("RETR %s\r\n", path); + fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (tmp->ptr[0] == '5') { + fclose(ftp->data); + ftp->data = NULL; + return -1; + } + return 0; +} + +int +Ftpfclose(FILE * f) +{ + fclose(f); + if (f == ftp->data) + ftp->data = NULL; + read_response(ftp); + return 0; +} + +int +FtpData(FTP ftp, char *cmd, char *arg, char *mode) +{ + Str tmp; + + if (ftp_pasv(ftp) < 0) + return -1; + tmp = Sprintf(cmd, arg); + Strcat_charp(tmp, "\r\n"); + fwrite(tmp->ptr, tmp->length, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (tmp->ptr[0] == '5') { + fclose(ftp->data); + ftp->data = NULL; + return -1; + } + return 0; +} + +int +FtpClose(FTP ftp) +{ + Str tmp; + + fclose(ftp->data); + ftp->data = NULL; + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 226) + return -1; + return 0; +} + +int +FtpBye(FTP ftp) +{ + Str tmp; + int ret_val, control_closed; + + fwrite("QUIT\r\n", 6, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (atoi(tmp->ptr) != 221) + ret_val = -1; + else + ret_val = 0; + control_closed = 0; + if (ftp->rcontrol != NULL) { + fclose(ftp->rcontrol); + ftp->rcontrol = NULL; + control_closed = 1; + } + if (ftp->wcontrol != NULL) { + fclose(ftp->wcontrol); + ftp->wcontrol = NULL; + control_closed = 1; + } + if (control_closed && ftp->data != NULL) { + fclose(ftp->data); + ftp->data = NULL; + } + return ret_val; +} + + +Str FTPDIRtmp; + +static int ex_ftpdir_name_size_date(char *, char **, char **, char **); +static int ftp_system(FTP); +static char *ftp_escape_str(char *); +static char *ftp_restore_str(char *); + +#define SERVER_NONE 0 +#define UNIXLIKE_SERVER 1 + +#define FTPDIR_NONE 0 +#define FTPDIR_DIR 1 +#define FTPDIR_LINK 2 +#define FTPDIR_FILE 3 + +FILE * +openFTP(ParsedURL * pu) +{ + Str tmp2 = Strnew(); + Str tmp3 = Strnew(); + Str host; + STATUS s; + Str curdir; + char *user; + char *pass; + char *fn; + char *qdir; + char **flist; + int i, nfile, nfile_max = 100; + Str pwd; + int add_auth_cookie_flag; + char *realpath = NULL; +#ifdef JP_CHARSET + char code = '\0', ic; + Str pathStr; +#endif + int sv_type; + + add_auth_cookie_flag = 0; + if (pu->user) + user = pu->user; + else { + Strcat_charp(tmp3, "anonymous"); + user = tmp3->ptr; + } + if (pu->pass) + pass = pu->pass; + else if (pu->user) { + pwd = find_auth_cookie(pu->host, pu->user); + if (pwd == NULL) { + if (fmInitialized) { + term_raw(); + pwd = Strnew_charp(inputLine("Password: ", NULL, IN_PASSWORD)); + term_cbreak(); + } + else { + pwd = Strnew_charp((char *) getpass("Password: ")); + } + add_auth_cookie_flag = 1; + } + pass = pwd->ptr; + } + else if (ftppasswd != NULL && *ftppasswd != '\0') + pass = ftppasswd; + else { + struct passwd *mypw = getpwuid(getuid()); + if (mypw == NULL) + Strcat_charp(tmp2, "anonymous"); + else + Strcat_charp(tmp2, mypw->pw_name); + Strcat_char(tmp2, '@'); + pass = tmp2->ptr; + } + s = FtpLogin(&ftp, pu->host, user, pass); + if (FtpError(s)) + return NULL; + if (add_auth_cookie_flag) + add_auth_cookie(pu->host, pu->user, pwd); + if (pu->file == NULL || *pu->file == '\0') + goto ftp_dir; + else + realpath = ftp_restore_str(pu->file); + + /* Get file */ + FtpBinary(ftp); + s = FtpOpenRead(ftp, realpath); + if (!FtpError(s)) { +#ifdef JP_CHARSET + pathStr = Strnew_charp(realpath); + if ((ic = checkShiftCode(pathStr, code)) != '\0') { + pathStr = conv_str(pathStr, (code = ic), InnerCode); + realpath = pathStr->ptr; + } +#endif /* JP_CHARSET */ + pu->file = realpath; + return FTPDATA(ftp); + } + + /* Get directory */ + ftp_dir: + pu->scheme = SCM_FTPDIR; + FTPDIRtmp = Strnew(); + sv_type = ftp_system(ftp); + if (pu->file == NULL || *pu->file == '\0') { + if (sv_type == UNIXLIKE_SERVER) { + s = FtpData(ftp, "LIST", NULL, "r"); + } + else { + s = FtpData(ftp, "NLST", NULL, "r"); + } + curdir = Strnew_charp("/"); + } + else { + if (sv_type == UNIXLIKE_SERVER) { + s = FtpCwd(ftp, realpath); + if (!FtpError(s)) { + s = FtpData(ftp, "LIST", NULL, "r"); + } + } + else { + s = FtpData(ftp, "NLST %s", realpath, "r"); + } + if (realpath[0] == '/') + curdir = Strnew_charp(realpath); + else + curdir = Sprintf("/%s", realpath); + if (Strlastchar(curdir) != '/') + Strcat_char(curdir, '/'); + } + if (FtpError(s)) { + FtpBye(ftp); + return NULL; + } + host = Strnew_charp("ftp://"); + if (pu->user) { + Strcat_m_charp(host, pu->user, "@", NULL); + } + Strcat_charp(host, pu->host); + if (Strlastchar(host) == '/') + Strshrink(host, 1); + qdir = htmlquote_str(curdir->ptr); + FTPDIRtmp = Sprintf("<html><head><title>%s%s</title></head><body><h1>Index of %s%s</h1>\n", + host->ptr, qdir, host->ptr, qdir); + curdir = Strnew_charp(ftp_escape_str(curdir->ptr)); + qdir = curdir->ptr; + tmp2 = Strdup(curdir); + if (Strcmp_charp(curdir, "/") != 0) { + Strshrink(tmp2, 1); + while (Strlastchar(tmp2) != '/' && tmp2->length > 0) + Strshrink(tmp2, 1); + } + if (sv_type == UNIXLIKE_SERVER) { + Strcat_charp(FTPDIRtmp, "<pre><a href=\""); + } + else { + Strcat_charp(FTPDIRtmp, "<ul><li><a href=\""); + } + Strcat_m_charp(FTPDIRtmp, host->ptr, + htmlquote_str(tmp2->ptr), + "\">[Upper Directory]</a>\n", NULL); + + flist = New_N(char *, nfile_max); + nfile = 0; + if (sv_type == UNIXLIKE_SERVER) { + char *name, *date, *size, *type_str; + int ftype, max_len, len, i, j; + Str line_tmp; + + max_len = 0; + while (tmp2 = Strfgets(FTPDATA(ftp)), tmp2->length > 0) { + Strchop(tmp2); + if ((ftype = ex_ftpdir_name_size_date(tmp2->ptr, &name, &date, &size)) + == FTPDIR_NONE) { + continue; + } + if (!strcmp(".", name) || !strcmp("..", name)) { + continue; + } + len = strlen(name); + if (!len) + continue; + if (ftype == FTPDIR_DIR) { + len++; + type_str = "/"; + } + else if (ftype == FTPDIR_LINK) { + len++; + type_str = "@"; + } + else { + type_str = ""; + } + if (max_len < len) + max_len = len; + line_tmp = Sprintf("%s%s %-12.12s %6.6s", name, type_str, date, size); + flist[nfile++] = line_tmp->ptr; + if (nfile == nfile_max) { + nfile_max *= 2; + flist = New_Reuse(char *, flist, nfile_max); + } + } + qsort(flist, nfile, sizeof(char *), strCmp); + for (j = 0; j < nfile; j++) { + fn = flist[j]; + date = fn + strlen(fn) - 20; + if (*(date - 1) == '/') { + ftype = FTPDIR_DIR; + *(date - 1) = '\0'; + } + else if (*(date - 1) == '@') { + ftype = FTPDIR_LINK; + *(date - 1) = '\0'; + } + else { + ftype = FTPDIR_FILE; + *date = '\0'; + } + date++; + len = strlen(fn); + Strcat_m_charp(FTPDIRtmp, "<a href=\"", + host->ptr, + qdir, + ftp_escape_str(fn), + "\">", + htmlquote_str(fn), NULL); + if (ftype == FTPDIR_DIR) { + Strcat_charp(FTPDIRtmp, "/"); + len++; + } + else if (ftype == FTPDIR_LINK) { + Strcat_charp(FTPDIRtmp, "@"); + len++; + } + Strcat_charp(FTPDIRtmp, "</a>"); + for (i = len; i <= max_len; i++) { + if ((max_len % 2 + i) % 2) { + Strcat_charp(FTPDIRtmp, "."); + } + else { + Strcat_charp(FTPDIRtmp, " "); + } + } + Strcat_m_charp(FTPDIRtmp, date, "\n", NULL); + } + Strcat_charp(FTPDIRtmp, "</pre></body></html>\n"); + } + else { + while (tmp2 = Strfgets(FTPDATA(ftp)), tmp2->length > 0) { + Strchop(tmp2); + flist[nfile++] = mybasename(tmp2->ptr); + if (nfile == nfile_max) { + nfile_max *= 2; + flist = New_Reuse(char *, flist, nfile_max); + } + } + qsort(flist, nfile, sizeof(char *), strCmp); + for (i = 0; i < nfile; i++) { + fn = flist[i]; + Strcat_m_charp(FTPDIRtmp, "<li><a href=\"", + host->ptr, qdir, + ftp_escape_str(fn), + "\">", + htmlquote_str(fn), + "</a>\n", NULL); + } + Strcat_charp(FTPDIRtmp, "</ul></body></html>\n"); + } + + FtpClose(ftp); + FtpBye(ftp); + return NULL; +} + +static int +ftp_system(FTP ftp) +{ + int sv_type = SERVER_NONE; + Str tmp; + + fwrite("SYST\r\n", 6, sizeof(char), ftp->wcontrol); + fflush(ftp->wcontrol); + tmp = read_response(ftp); + if (strstr(tmp->ptr, "UNIX") != NULL + || !strncmp(tmp->ptr + 4, "Windows_NT", 10)) { /* :-) */ + sv_type = UNIXLIKE_SERVER; + } + + return (sv_type); +} + +static char * +ftp_escape_str(char *str) +{ + Str s = Strnew(); + char *p, buf[5]; + unsigned char c; + + for (; (c = (unsigned char) *str) != '\0'; str++) { + p = NULL; + if (c < '!' || c > '~' + || c == '#' || c == '?' || c == '+' + || c == '&' || c == '<' || c == '>' || c == '"' || c == '%') { + sprintf(buf, "%%%02X", c); + p = buf; + } + if (p) + Strcat_charp(s, p); + else + Strcat_char(s, *str); + } + return s->ptr; +} + +#define XD_CTOD(c) {\ + if (c >= '0' && c <= '9') {\ + c -= (unsigned char)'0';\ + } else if (c >= 'a' && c <= 'f') {\ + c = c - (unsigned char)'a' + (unsigned char)10;\ + } else if (c >= 'A' && c <= 'F') {\ + c = c - (unsigned char)'A' + (unsigned char)10;\ + } else {\ + goto skip;\ + }\ +} + +static char * +ftp_restore_str(char *str) +{ + Str s = Strnew(); + char *p; + unsigned char c, code[2]; + + for (; *str; str++) { + p = NULL; + if (*str == '%' && str[1] != '\0' && str[2] != '\0') { + c = (unsigned char) str[1]; + XD_CTOD(c) + code[0] = c * (unsigned char) 16; + c = (unsigned char) str[2]; + XD_CTOD(c) + code[0] += c; + code[1] = '\0'; + p = (char *) code; + str += 2; + } + skip: + if (p) + Strcat_charp(s, p); + else + Strcat_char(s, *str); + } + return s->ptr; +} + +#define EX_SKIP_SPACE(cp) {\ + while (IS_SPACE(*cp) && *cp != '\0') cp++;\ + if (*cp == '\0') {\ + goto done;\ + }\ +} +#define EX_SKIP_NONE_SPACE(cp) {\ + while (!IS_SPACE(*cp) && *cp != '\0') cp++;\ + if (*cp == '\0') {\ + goto done;\ + }\ +} + +static Str size_int2str(unsigned long); + +static int +ex_ftpdir_name_size_date(char *line, char **name, char **date, char **sizep) +{ + int ftype = FTPDIR_NONE; + char *cp, *endp; + Str date_str, name_str, size_str; + unsigned long size; + + if (strlen(line) < 11) { + goto done; + } + /* skip permission */ + if (!IS_SPACE(line[10])) { + goto done; + } + cp = line + 11; + + /* skip link count */ + EX_SKIP_SPACE(cp) + while (IS_DIGIT(*cp) && *cp != '\0') + cp++; + if (!IS_SPACE(*cp) || *cp == '\0') { + goto done; + } + cp++; + + /* skip owner string */ + EX_SKIP_SPACE(cp) + EX_SKIP_NONE_SPACE(cp) + cp++; + + /* skip group string */ + EX_SKIP_SPACE(cp) + EX_SKIP_NONE_SPACE(cp) + cp++; + + /* extract size */ + EX_SKIP_SPACE(cp) + size = 0; + while (*cp && IS_DIGIT(*cp)) { + size = size * 10 + *(cp++) - '0'; + } + if (*cp == '\0') { + goto done; + } + + /* extract date */ + EX_SKIP_SPACE(cp) + if (IS_ALPHA(cp[0]) && IS_ALPHA(cp[1]) && IS_ALPHA(cp[2]) + && IS_SPACE(cp[3]) + && (IS_SPACE(cp[4]) || IS_DIGIT(cp[4])) && IS_DIGIT(cp[5]) + && IS_SPACE(cp[6]) + && (IS_SPACE(cp[7]) || IS_DIGIT(cp[7])) && IS_DIGIT(cp[8]) + && (cp[9] == ':' || IS_DIGIT(cp[9])) + && IS_DIGIT(cp[10]) && IS_DIGIT(cp[11]) + && IS_SPACE(cp[12])) { + cp[12] = '\0'; + date_str = Strnew_charp(cp); + cp += 13; + } + else { + goto done; + } + + /* extract file name */ + EX_SKIP_SPACE(cp) + if (line[0] == 'l') { + if ((endp = strstr(cp, " -> ")) == NULL) { + goto done; + } + *endp = '\0'; + size_str = Strnew_charp("-"); + ftype = FTPDIR_LINK; + } + else if (line[0] == 'd') { + size_str = Strnew_charp("-"); + ftype = FTPDIR_DIR; + } + else { + size_str = size_int2str(size); + ftype = FTPDIR_FILE; + } + name_str = Strnew_charp(cp); + *date = date_str->ptr; + *name = name_str->ptr; + *sizep = size_str->ptr; + + done: + return (ftype); +} + +static Str +size_int2str(unsigned long size) +{ + Str size_str; + int unit; + double dtmp; + char *size_format, *unit_str; + + dtmp = (double) size; + for (unit = 0; unit < 3; unit++) { + if (dtmp < 1024) { + break; + } + dtmp /= 1024; + } + if (!unit || dtmp > 100) { + size_format = "%.0f%s"; + } + else if (dtmp > 10) { + size_format = "%.1f%s"; + } + else { + size_format = "%.2f%s"; + } + switch (unit) { + case 3: + unit_str = "G"; + break; + case 2: + unit_str = "M"; + break; + case 1: + unit_str = "K"; + break; + default: + unit_str = ""; + break; + } + size_str = Sprintf(size_format, dtmp, unit_str); + + return (size_str); +} + +void +closeFTP(FILE * f) +{ + if (f) { + fclose(f); + if (f == ftp->data) + ftp->data = NULL; + } + FtpBye(ftp); +} @@ -0,0 +1,356 @@ +/* + * w3m func.c + */ + +#include <stdio.h> + +#include "fm.h" +#include "func.h" +#include "myctype.h" + +#include "funcname.c" +int w3mNFuncList = 0; + +KeyList w3mKeyList = { + NULL, 0, 0 +}; + +void +initKeymap(void) +{ + FILE *kf; + Str line; + char *p, *s, *emsg; + int c; + int f; + int lineno; + + if (!w3mNFuncList) + w3mNFuncList = countFuncList(w3mFuncList); + + if ((kf = fopen(rcFile(KEYMAP_FILE), "rt")) == NULL) + return; + + lineno = 0; + while (!feof(kf)) { + line = Strfgets(kf); + lineno++; + Strchop(line); + Strremovefirstspaces(line); + if (line->length == 0) + continue; + p = line->ptr; + s = getWord(&p); + if (*s == '#') /* comment */ + continue; + if (strcmp(s, "keymap")) { /* error */ + emsg = Sprintf("line %d: keymap not found\n", lineno)->ptr; + record_err_message(emsg); + disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE); + continue; + } + s = getQWord(&p); + c = getKey(s); + if (c < 0) { /* error */ + emsg = Sprintf("line %d: unknown key '%s'\n", lineno, s)->ptr; + record_err_message(emsg); + disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE); + continue; + } + s = getWord(&p); + f = getFuncList(s, w3mFuncList, w3mNFuncList); + if (f < 0) { + emsg = Sprintf("line %d: invalid command '%s'\n", lineno, s)->ptr; + record_err_message(emsg); + disp_message_nsec(emsg, FALSE, 1, TRUE, FALSE); + f = FUNCNAME_nulcmd; + } + if (c & K_ESCD) + EscDKeymap[c ^ K_ESCD] = (f >= 0) ? f : FUNCNAME_nulcmd; + else if (c & K_ESCB) + EscBKeymap[c ^ K_ESCB] = (f >= 0) ? f : FUNCNAME_nulcmd; + else if (c & K_ESC) + EscKeymap[c ^ K_ESC] = (f >= 0) ? f : FUNCNAME_nulcmd; + else + GlobalKeymap[c] = (f >= 0) ? f : FUNCNAME_nulcmd; + s = getQWord(&p); + addKeyList(&w3mKeyList, c, s); + } + fclose(kf); +} + +int +countFuncList(FuncList * list) +{ + int i; + + for (i = 0; list->id != NULL; i++, list++); + return i; +} + +int +getFuncList(char *id, FuncList * list, int nlist) +{ + int i, is, ie, m; + + if (id == NULL || *id == '\0' || nlist <= 0) + return -1; + + is = 0; + ie = nlist - 1; + while (1) { + i = is + (ie - is) / 2; + if ((m = strcmp(id, list[i].id)) == 0) + return i; + else if (is >= ie) + return -1; + else if (m > 0) + is = i + 1; + else + ie = i - 1; + } +} + +int +getKey(char *s) +{ + int c, esc = 0, ctrl = 0; + + if (s == NULL || *s == '\0') + return -1; + + if (strcasecmp(s, "UP") == 0) /* ^[[A */ + return K_ESCB | 'A'; + else if (strcasecmp(s, "DOWN") == 0) /* ^[[B */ + return K_ESCB | 'B'; + else if (strcasecmp(s, "RIGHT") == 0) /* ^[[C */ + return K_ESCB | 'C'; + else if (strcasecmp(s, "LEFT") == 0) /* ^[[D */ + return K_ESCB | 'D'; + + if (strncasecmp(s, "ESC-", 4) == 0 || + strncasecmp(s, "ESC ", 4) == 0) { /* ^[ */ + s += 4; + esc = K_ESC; + } + else if (strncasecmp(s, "M-", 2) == 0 || + strncasecmp(s, "\\E", 2) == 0) { /* ^[ */ + s += 2; + esc = K_ESC; + } + else if (*s == ESC_CODE) { /* ^[ */ + s++; + esc = K_ESC; + } + if (strncasecmp(s, "C-", 2) == 0) { /* ^, ^[^ */ + s += 2; + ctrl = 1; + } + else if (*s == '^') { /* ^, ^[^ */ + s++; + ctrl = 1; + } + if (!esc && ctrl && *s == '[') { /* ^[ */ + s++; + ctrl = 0; + esc = K_ESC; + } + if (esc && !ctrl) { + if (*s == '[' || *s == 'O') { /* ^[[, ^[O */ + s++; + esc = K_ESCB; + } + if (strncasecmp(s, "C-", 2) == 0) { /* ^[^, ^[[^ */ + s += 2; + ctrl = 1; + } + else if (*s == '^') { /* ^[^, ^[[^ */ + s++; + ctrl = 1; + } + } + + if (ctrl) { + if (*s >= '@' && *s <= '_') /* ^@ .. ^_ */ + return esc | (*s - '@'); + else if (*s >= 'a' && *s <= 'z') /* ^a .. ^z */ + return esc | (*s - 'a' + 1); + else if (*s == '?') /* ^? */ + return esc | DEL_CODE; + else + return -1; + } + + if (esc == K_ESCB && IS_DIGIT(*s)) { + c = (int)(*s - '0'); + s++; + if (IS_DIGIT(*s)) { + c = c * 10 + (int)(*s - '0'); + s++; + } + if (*s == '~') + return K_ESCD | c; + else + return -1; + } + + if (strncasecmp(s, "SPC", 3) == 0) /* ' ' */ + return esc | ' '; + else if (strncasecmp(s, "TAB", 3) == 0) /* ^i */ + return esc | '\t'; + else if (strncasecmp(s, "DEL", 3) == 0) /* ^? */ + return esc | DEL_CODE; + + if (*s == '\\' && *(s + 1) != '\0') { + switch (*(s + 1)) { + case 'a': /* ^g */ + return esc | CTRL_G; + case 'b': /* ^h */ + return esc | CTRL_H; + case 't': /* ^i */ + return esc | CTRL_I; + case 'n': /* ^j */ + return esc | CTRL_J; + case 'r': /* ^m */ + return esc | CTRL_M; + case 'e': /* ^[ */ + return esc | ESC_CODE; + case '^': /* ^ */ + return esc | '^'; + case '\\': /* \ */ + return esc | '\\'; + default: + return -1; + } + } + if (IS_ASCII(*s)) /* Ascii */ + return esc | *s; + else + return -1; +} + +void +addKeyList(KeyList *list, int key, char *data) +{ + KeyListItem *item; + + if (data == NULL || *data == '\0') + data = NULL; + else + data = allocStr(data, 0); + item = searchKeyList(list, key); + if (item == NULL) { + if (data == NULL) + return; + list->nitem++; + if (list->nitem > list->size) { + list->size = (list->size >= 2) ? (list->size * 3 / 2) : 2; + list->item = New_Reuse(KeyListItem, list->item, + list->size * sizeof(KeyListItem)); + } + item = &(list->item[list->nitem - 1]); + item->key = key; + } + item->data = data; +} + +KeyListItem * +searchKeyList(KeyList *list, int key) +{ + int i; + KeyListItem *item; + + if (list == NULL) + return NULL; + for (i = 0, item = list->item; i < list->nitem; i++, item++) { + if (key == item->key) + return item; + } + return NULL; +} + +char * +getWord(char **str) +{ + char *p, *s; + + p = *str; + SKIP_BLANKS(p); + s = p; + while (*p != '\0') { + if (IS_SPACE(*p)) { + *p = '\0'; + p++; + break; + } + p++; + } + *str = p; + return s; +} + +char * +getQWord(char **str) +{ + char *p, *s, *e; + int in_q = 0, in_dq = 0, esc = 0; + + p = *str; + while (*p && IS_SPACE(*p)) + p++; + s = p; + e = p; + while (*p != '\0') { + if (esc) { + if (in_q) { + if (*p != '\\' && *p != '\'') /* '..\\..', '..\'..' */ + *e++ = '\\'; + } + else if (in_dq) { + if (*p != '\\' && *p != '"') /* "..\\..", "..\".." */ + *e++ = '\\'; + } + else { + if (*p != '\\' && *p != '\'' && /* ..\\.., * + * ..\'.. */ + *p != '"' && !IS_SPACE(*p)) /* ..\".., * ..\.. + */ + *e++ = '\\'; + } + *e++ = *p; + esc = 0; + } + else if (*p == '\\') { + esc = 1; + } + else if (in_q) { + if (*p == '\'') + in_q = 0; + else + *e++ = *p; + } + else if (in_dq) { + if (*p == '"') + in_dq = 0; + else + *e++ = *p; + } + else if (*p == '\'') { + in_q = 1; + } + else if (*p == '"') { + in_dq = 1; + } + else if (IS_SPACE(*p)) { + p++; + break; + } + else { + *e++ = *p; + } + p++; + } + *e = '\0'; + *str = p; + return s; +} @@ -0,0 +1,28 @@ +/* + * w3m func.h + */ + +#ifndef FUNC_H +#define FUNC_H + +#define K_ESC 0x100 +#define K_ESCB 0x200 +#define K_ESCD 0x400 + +typedef struct _FuncList { + char *id; + void (*func) (); +} FuncList; + +typedef struct _KeyListItem { + int key; + char *data; +} KeyListItem; + +typedef struct _KeyList { + KeyListItem *item; + int nitem; + int size; +} KeyList; + +#endif /* not FUNC_H */ diff --git a/funcname.c b/funcname.c new file mode 100644 index 0000000..32fad64 --- /dev/null +++ b/funcname.c @@ -0,0 +1,98 @@ +FuncList w3mFuncList[] = { +/*0*/ {"@@@",nulcmd}, +/*1*/ {"ABORT",quitfm}, +/*2*/ {"ADD_BOOKMARK",adBmark}, +/*3*/ {"BACK",backBf}, +/*4*/ {"BEGIN",goLineF}, +/*5*/ {"BOOKMARK",ldBmark}, +/*6*/ {"CENTER_H",ctrCsrH}, +/*7*/ {"CENTER_V",ctrCsrV}, +/*8*/ {"COOKIE",cooLst}, +/*9*/ {"DELETE_PREVBUF",deletePrevBuf}, +/*10*/ {"DICT_WORD",dictword}, +/*11*/ {"DICT_WORD_AT",dictwordat}, +/*12*/ {"DOWN",ldown1}, +/*13*/ {"DOWNLOAD",svSrc}, +/*14*/ {"EDIT",editBf}, +/*15*/ {"EDIT_SCREEN",editScr}, +/*16*/ {"END",goLineL}, +/*17*/ {"ESCBMAP",escbmap}, +/*18*/ {"ESCMAP",escmap}, +/*19*/ {"EXEC_SHELL",execsh}, +/*20*/ {"EXIT",quitfm}, +/*21*/ {"EXTERN",extbrz}, +/*22*/ {"EXTERN_LINK",linkbrz}, +/*23*/ {"FRAME",rFrame}, +/*24*/ {"GOTO",goURL}, +/*25*/ {"GOTO_LINE",goLine}, +/*26*/ {"GOTO_LINK",followA}, +/*27*/ {"HELP",ldhelp}, +/*28*/ {"HISTORY",ldHist}, +/*29*/ {"INFO",pginfo}, +/*30*/ {"INIT_MAILCAP",initMailcap}, +/*31*/ {"INTERRUPT",susp}, +/*32*/ {"LEFT",col1L}, +/*33*/ {"LINE_BEGIN",linbeg}, +/*34*/ {"LINE_END",linend}, +/*35*/ {"LINE_INFO",curlno}, +/*36*/ {"LINK_BEGIN",topA}, +/*37*/ {"LINK_END",lastA}, +/*38*/ {"LOAD",ldfile}, +/*39*/ {"MAIN_MENU",mainMn}, +/*40*/ {"MARK",_mark}, +/*41*/ {"MARK_MID",chkNMID}, +/*42*/ {"MARK_URL",chkURL}, +/*43*/ {"MENU",mainMn}, +/*44*/ {"MOUSE",mouse}, +/*45*/ {"MOUSE_TOGGLE",msToggle}, +/*46*/ {"MOVE_DOWN",movD}, +/*47*/ {"MOVE_LEFT",movL}, +/*48*/ {"MOVE_RIGHT",movR}, +/*49*/ {"MOVE_UP",movU}, +/*50*/ {"MSGS",msgs}, +/*51*/ {"NEXT_LINK",nextA}, +/*52*/ {"NEXT_MARK",nextMk}, +/*53*/ {"NEXT_PAGE",pgFore}, +/*54*/ {"NEXT_WORD",movRW}, +/*55*/ {"NOTHING",nulcmd}, +/*56*/ {"NULL",nulcmd}, +/*57*/ {"OPTIONS",ldOpt}, +/*58*/ {"PCMAP",pcmap}, +/*59*/ {"PEEK",curURL}, +/*60*/ {"PEEK_LINK",peekURL}, +/*61*/ {"PEEK_IMG",peekIMG}, +/*62*/ {"PIPE_SHELL",pipesh}, +/*63*/ {"PREV_LINK",prevA}, +/*64*/ {"PREV_MARK",prevMk}, +/*65*/ {"PREV_PAGE",pgBack}, +/*66*/ {"PREV_WORD",movLW}, +/*67*/ {"PRINT",svBuf}, +/*68*/ {"QUIT",qquitfm}, +/*69*/ {"READ_SHELL",readsh}, +/*70*/ {"REDRAW",rdrwSc}, +/*71*/ {"REG_MARK",reMark}, +/*72*/ {"RELOAD",reload}, +/*73*/ {"RIGHT",col1R}, +/*74*/ {"SAVE",svSrc}, +/*75*/ {"SAVE_IMAGE",svI}, +/*76*/ {"SAVE_LINK",svA}, +/*77*/ {"SAVE_SCREEN",svBuf}, +/*78*/ {"SEARCH",srchfor}, +/*79*/ {"SEARCH_BACK",srchbak}, +/*80*/ {"SEARCH_FORE",srchfor}, +/*81*/ {"SEARCH_NEXT",srchnxt}, +/*82*/ {"SEARCH_PREV",srchprv}, +/*83*/ {"SELECT",selBuf}, +/*84*/ {"SHELL",execsh}, +/*85*/ {"SHIFT_LEFT",shiftl}, +/*86*/ {"SHIFT_RIGHT",shiftr}, +/*87*/ {"SOURCE",vwSrc}, +/*88*/ {"SUSPEND",susp}, +/*89*/ {"UP",lup1}, +/*90*/ {"VIEW",vwSrc}, +/*91*/ {"VIEW_BOOKMARK",ldBmark}, +/*92*/ {"VIEW_IMAGE",followI}, +/*93*/ {"WHEREIS",srchfor}, +/*94*/ {"WRAP_TOGGLE",wrapToggle}, +{ NULL, NULL } +}; diff --git a/funcname.tab b/funcname.tab new file mode 100644 index 0000000..8cf8be8 --- /dev/null +++ b/funcname.tab @@ -0,0 +1,97 @@ +# macro name function name +#---------------------------- +@@@ nulcmd +ABORT quitfm +ADD_BOOKMARK adBmark +BACK backBf +BEGIN goLineF +BOOKMARK ldBmark +CENTER_H ctrCsrH +CENTER_V ctrCsrV +COOKIE cooLst +DELETE_PREVBUF deletePrevBuf +DICT_WORD dictword +DICT_WORD_AT dictwordat +DOWN ldown1 +DOWNLOAD svSrc +EDIT editBf +EDIT_SCREEN editScr +END goLineL +ESCBMAP escbmap +ESCMAP escmap +EXEC_SHELL execsh +EXIT quitfm +EXTERN extbrz +EXTERN_LINK linkbrz +FRAME rFrame +GOTO goURL +GOTO_LINE goLine +GOTO_LINK followA +HELP ldhelp +HISTORY ldHist +INFO pginfo +INIT_MAILCAP initMailcap +INTERRUPT susp +LEFT col1L +LINE_BEGIN linbeg +LINE_END linend +LINE_INFO curlno +LINK_BEGIN topA +LINK_END lastA +LOAD ldfile +MAIN_MENU mainMn +MARK _mark +MARK_MID chkNMID +MARK_URL chkURL +MENU mainMn +MOUSE mouse +MOUSE_TOGGLE msToggle +MOVE_DOWN movD +MOVE_LEFT movL +MOVE_RIGHT movR +MOVE_UP movU +MSGS msgs +NEXT_LINK nextA +NEXT_MARK nextMk +NEXT_PAGE pgFore +NEXT_WORD movRW +NOTHING nulcmd +NULL nulcmd +OPTIONS ldOpt +PCMAP pcmap +PEEK curURL +PEEK_LINK peekURL +PEEK_IMG peekIMG +PIPE_SHELL pipesh +PREV_LINK prevA +PREV_MARK prevMk +PREV_PAGE pgBack +PREV_WORD movLW +PRINT svBuf +QUIT qquitfm +READ_SHELL readsh +REDRAW rdrwSc +REG_MARK reMark +RELOAD reload +RIGHT col1R +SAVE svSrc +SAVE_IMAGE svI +SAVE_LINK svA +SAVE_SCREEN svBuf +SEARCH srchfor +SEARCH_BACK srchbak +SEARCH_FORE srchfor +SEARCH_NEXT srchnxt +SEARCH_PREV srchprv +SELECT selBuf +SHELL execsh +SHIFT_LEFT shiftl +SHIFT_RIGHT shiftr +SOURCE vwSrc +SUSPEND susp +UP lup1 +VIEW vwSrc +VIEW_BOOKMARK ldBmark +VIEW_IMAGE followI +WHEREIS srchfor +WRAP_TOGGLE wrapToggle diff --git a/funcname0.awk b/funcname0.awk new file mode 100644 index 0000000..9a9152f --- /dev/null +++ b/funcname0.awk @@ -0,0 +1,13 @@ +BEGIN { + print "FuncList w3mFuncList[] = {"; + n = 0; +} +/^#/ { next } +{ + print "/*" n "*/ {\"" $1 "\"," $2 "},"; + n++; +} +END { + print "{ NULL, NULL }" + print "};" +} diff --git a/funcname1.awk b/funcname1.awk new file mode 100644 index 0000000..343cf48 --- /dev/null +++ b/funcname1.awk @@ -0,0 +1,10 @@ +BEGIN { n=0 } +/^#/ {next} +{ + if (cmd[$2] == "") { + print "#define FUNCNAME_" $2 " " n; + cmd[$2] = n; + } + n++; +} + diff --git a/funcname1.h b/funcname1.h new file mode 100644 index 0000000..806c8d4 --- /dev/null +++ b/funcname1.h @@ -0,0 +1,83 @@ +#define FUNCNAME_nulcmd 0 +#define FUNCNAME_quitfm 1 +#define FUNCNAME_adBmark 2 +#define FUNCNAME_backBf 3 +#define FUNCNAME_goLineF 4 +#define FUNCNAME_ldBmark 5 +#define FUNCNAME_ctrCsrH 6 +#define FUNCNAME_ctrCsrV 7 +#define FUNCNAME_cooLst 8 +#define FUNCNAME_deletePrevBuf 9 +#define FUNCNAME_dictword 10 +#define FUNCNAME_dictwordat 11 +#define FUNCNAME_ldown1 12 +#define FUNCNAME_svSrc 13 +#define FUNCNAME_editBf 14 +#define FUNCNAME_editScr 15 +#define FUNCNAME_goLineL 16 +#define FUNCNAME_escbmap 17 +#define FUNCNAME_escmap 18 +#define FUNCNAME_execsh 19 +#define FUNCNAME_extbrz 21 +#define FUNCNAME_linkbrz 22 +#define FUNCNAME_rFrame 23 +#define FUNCNAME_goURL 24 +#define FUNCNAME_goLine 25 +#define FUNCNAME_followA 26 +#define FUNCNAME_ldhelp 27 +#define FUNCNAME_ldHist 28 +#define FUNCNAME_pginfo 29 +#define FUNCNAME_initMailcap 30 +#define FUNCNAME_susp 31 +#define FUNCNAME_col1L 32 +#define FUNCNAME_linbeg 33 +#define FUNCNAME_linend 34 +#define FUNCNAME_curlno 35 +#define FUNCNAME_topA 36 +#define FUNCNAME_lastA 37 +#define FUNCNAME_ldfile 38 +#define FUNCNAME_mainMn 39 +#define FUNCNAME__mark 40 +#define FUNCNAME_chkNMID 41 +#define FUNCNAME_chkURL 42 +#define FUNCNAME_mouse 44 +#define FUNCNAME_msToggle 45 +#define FUNCNAME_movD 46 +#define FUNCNAME_movL 47 +#define FUNCNAME_movR 48 +#define FUNCNAME_movU 49 +#define FUNCNAME_msgs 50 +#define FUNCNAME_nextA 51 +#define FUNCNAME_nextMk 52 +#define FUNCNAME_pgFore 53 +#define FUNCNAME_movRW 54 +#define FUNCNAME_ldOpt 57 +#define FUNCNAME_pcmap 58 +#define FUNCNAME_curURL 59 +#define FUNCNAME_peekURL 60 +#define FUNCNAME_peekIMG 61 +#define FUNCNAME_pipesh 62 +#define FUNCNAME_prevA 63 +#define FUNCNAME_prevMk 64 +#define FUNCNAME_pgBack 65 +#define FUNCNAME_movLW 66 +#define FUNCNAME_svBuf 67 +#define FUNCNAME_qquitfm 68 +#define FUNCNAME_readsh 69 +#define FUNCNAME_rdrwSc 70 +#define FUNCNAME_reMark 71 +#define FUNCNAME_reload 72 +#define FUNCNAME_col1R 73 +#define FUNCNAME_svI 75 +#define FUNCNAME_svA 76 +#define FUNCNAME_srchfor 78 +#define FUNCNAME_srchbak 79 +#define FUNCNAME_srchnxt 81 +#define FUNCNAME_srchprv 82 +#define FUNCNAME_selBuf 83 +#define FUNCNAME_shiftl 85 +#define FUNCNAME_shiftr 86 +#define FUNCNAME_vwSrc 87 +#define FUNCNAME_lup1 89 +#define FUNCNAME_followI 92 +#define FUNCNAME_wrapToggle 94 diff --git a/funcname2.awk b/funcname2.awk new file mode 100644 index 0000000..231114e --- /dev/null +++ b/funcname2.awk @@ -0,0 +1,10 @@ +BEGIN { n=0 } +/^#/ {next} +{ + if (cmd[$2] == "") { + print "#define " $2 " " n; + cmd[$2] = n; + } + n++; +} + diff --git a/funcname2.h b/funcname2.h new file mode 100644 index 0000000..58b9cd4 --- /dev/null +++ b/funcname2.h @@ -0,0 +1,83 @@ +#define nulcmd 0 +#define quitfm 1 +#define adBmark 2 +#define backBf 3 +#define goLineF 4 +#define ldBmark 5 +#define ctrCsrH 6 +#define ctrCsrV 7 +#define cooLst 8 +#define deletePrevBuf 9 +#define dictword 10 +#define dictwordat 11 +#define ldown1 12 +#define svSrc 13 +#define editBf 14 +#define editScr 15 +#define goLineL 16 +#define escbmap 17 +#define escmap 18 +#define execsh 19 +#define extbrz 21 +#define linkbrz 22 +#define rFrame 23 +#define goURL 24 +#define goLine 25 +#define followA 26 +#define ldhelp 27 +#define ldHist 28 +#define pginfo 29 +#define initMailcap 30 +#define susp 31 +#define col1L 32 +#define linbeg 33 +#define linend 34 +#define curlno 35 +#define topA 36 +#define lastA 37 +#define ldfile 38 +#define mainMn 39 +#define _mark 40 +#define chkNMID 41 +#define chkURL 42 +#define mouse 44 +#define msToggle 45 +#define movD 46 +#define movL 47 +#define movR 48 +#define movU 49 +#define msgs 50 +#define nextA 51 +#define nextMk 52 +#define pgFore 53 +#define movRW 54 +#define ldOpt 57 +#define pcmap 58 +#define curURL 59 +#define peekURL 60 +#define peekIMG 61 +#define pipesh 62 +#define prevA 63 +#define prevMk 64 +#define pgBack 65 +#define movLW 66 +#define svBuf 67 +#define qquitfm 68 +#define readsh 69 +#define rdrwSc 70 +#define reMark 71 +#define reload 72 +#define col1R 73 +#define svI 75 +#define svA 76 +#define srchfor 78 +#define srchbak 79 +#define srchnxt 81 +#define srchprv 82 +#define selBuf 83 +#define shiftl 85 +#define shiftr 86 +#define vwSrc 87 +#define lup1 89 +#define followI 92 +#define wrapToggle 94 diff --git a/gc/BCC_MAKEFILE b/gc/BCC_MAKEFILE new file mode 100644 index 0000000..225a1ed --- /dev/null +++ b/gc/BCC_MAKEFILE @@ -0,0 +1,82 @@ +# Makefile for Borland C++ 4.5 on NT
+# For Borland 5.0, replace bc45 by bc5.
+# If you have the Borland assembler, remove "-DUSE_GENERIC"
+#
+bc= c:\bc45
+bcbin= $(bc)\bin
+bclib= $(bc)\lib
+bcinclude= $(bc)\include
+
+cc= $(bcbin)\bcc32
+rc= $(bcbin)\brc32
+lib= $(bcbin)\tlib
+link= $(bcbin)\tlink32
+cflags= -R -v -vi -H -H=gc.csm -I$(bcinclude);cord -L$(bclib) \
+ -w-pro -w-aus -w-par -w-ccc -w-rch -a4 -D__STDC__=0
+#defines= -DSILENT
+defines= -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS -DUSE_GENERIC
+
+.c.obj:
+ $(cc) @&&|
+ $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.c
+|
+
+.cpp.obj:
+ $(cc) @&&|
+ $(cdebug) $(cflags) $(cvars) $(defines) -o$* -c $*.cpp
+|
+
+.rc.res:
+ $(rc) -i$(bcinclude) -r -fo$* $*.rc
+
+XXXOBJS= XXXalloc.obj XXXreclaim.obj XXXallchblk.obj XXXmisc.obj \
+ XXXmach_dep.obj XXXos_dep.obj XXXmark_rts.obj XXXheaders.obj XXXmark.obj \
+ XXXobj_map.obj XXXblacklst.obj XXXfinalize.obj XXXnew_hblk.obj \
+ XXXdbg_mlc.obj XXXmalloc.obj XXXstubborn.obj XXXdyn_load.obj \
+ XXXtypd_mlc.obj XXXptr_chck.obj XXXgc_cpp.obj XXXmallocx.obj
+
+OBJS= $(XXXOBJS:XXX=)
+
+all: gctest.exe cord\de.exe test_cpp.exe
+
+$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h gcconfig.h MAKEFILE
+
+gc.lib: $(OBJS)
+ -del gc.lib
+ tlib $* @&&|
+ $(XXXOBJS:XXX=+)
+|
+
+gctest.exe: test.obj gc.lib
+ $(cc) @&&|
+ $(cflags) -W -e$* test.obj gc.lib
+|
+
+cord\de.obj cord\de_win.obj: cord\cord.h cord\private\cord_pos.h cord\de_win.h \
+ cord\de_cmds.h
+
+cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj \
+ cord\de_win.res gc.lib
+ $(cc) @&&|
+ $(cflags) -W -e$* cord\cordbscs.obj cord\cordxtra.obj \
+ cord\de.obj cord\de_win.obj gc.lib
+|
+ $(rc) cord\de_win.res cord\de.exe
+
+gc_cpp.obj: gc_cpp.h gc.h
+
+gc_cpp.cpp: gc_cpp.cc
+ copy gc_cpp.cc gc_cpp.cpp
+
+test_cpp.cpp: test_cpp.cc
+ copy test_cpp.cc test_cpp.cpp
+
+test_cpp.exe: test_cpp.obj gc_cpp.h gc.h gc.lib
+ $(cc) @&&|
+ $(cflags) -W -e$* test_cpp.obj gc.lib
+|
+
+scratch:
+ -del *.obj *.res *.exe *.csm cord\*.obj cord\*.res cord\*.exe cord\*.csm
+
+
diff --git a/gc/EMX_MAKEFILE b/gc/EMX_MAKEFILE new file mode 100644 index 0000000..54a06ce --- /dev/null +++ b/gc/EMX_MAKEFILE @@ -0,0 +1,141 @@ +# +# OS/2 specific Makefile for the EMX environment +# +# You need GNU Make 3.71, gcc 2.5.7, emx 0.8h and GNU fileutils 3.9 +# or similar tools. C++ interface and de.exe weren't tested. +# +# Rename this file "Makefile". +# + +# Primary targets: +# gc.a - builds basic library +# c++ - adds C++ interface to library and include directory +# cords - adds cords (heavyweight strings) to library and include directory +# test - prints porting information, then builds basic version of gc.a, and runs +# some tests of collector and cords. Does not add cords or c++ interface to gc.a +# cord/de.exe - builds dumb editor based on cords. +CC= gcc +CXX=g++ +# Needed only for "make c++", which adds the c++ interface + +CFLAGS= -O -DALL_INTERIOR_POINTERS -DSILENT +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. +# -DSILENT disables statistics printing, and improves performance. +# -DCHECKSUMS reports on erroneously clear dirty bits, and unexpectedly +# altered stubborn objects, at substantial performance cost. +# -DFIND_LEAK causes the collector to assume that all inaccessible +# objects should have been explicitly deallocated, and reports exceptions +# -DSOLARIS_THREADS enables support for Solaris (thr_) threads. +# (Clients should also define SOLARIS_THREADS and then include +# gc.h before performing thr_ or GC_ operations.) +# -DALL_INTERIOR_POINTERS allows all pointers to the interior +# of objects to be recognized. (See gc_private.h for consequences.) +# -DSMALL_CONFIG tries to tune the collector for small heap sizes, +# usually causing it to use less space in such situations. +# Incremental collection no longer works in this case. +# -DDONT_ADD_BYTE_AT_END is meaningful only with +# -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS +# causes all objects to be padded so that pointers just past the end of +# an object can be recognized. This can be expensive. (The padding +# is normally more than one byte due to alignment constraints.) +# -DDONT_ADD_BYTE_AT_END disables the padding. + +AR= ar +RANLIB= ar s + +# Redefining srcdir allows object code for the nonPCR version of the collector +# to be generated in different directories +srcdir = . +VPATH = $(srcdir) + +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.o mallocx.o + +CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o + +CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \ + $(srcdir)/cord/cord_pos.h + +# Libraries needed for curses applications. Only needed for de. +CURSES= -lcurses -ltermlib + +# The following is irrelevant on most systems. But a few +# versions of make otherwise fork the shell specified in +# the SHELL environment variable. +SHELL= bash + +SPECIALCFLAGS = +# Alternative flags to the C compiler for mach_dep.c. +# Mach_dep.c often doesn't like optimization, and it's +# not time-critical anyway. + +all: gc.a gctest.exe + +$(OBJS) test.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h +# The dependency on Makefile is needed. Changing +# options such as -DSILENT affects the size of GC_arrays, +# invalidating all .o files that rely on gc_priv.h + +mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h + +gc.a: $(OBJS) + $(AR) ru gc.a $(OBJS) + $(RANLIB) gc.a + +cords: $(CORD_OBJS) cord/cordtest.exe + $(AR) ru gc.a $(CORD_OBJS) + $(RANLIB) gc.a + cp $(srcdir)/cord/cord.h include/cord.h + cp $(srcdir)/cord/ec.h include/ec.h + cp $(srcdir)/cord/cord_pos.h include/cord_pos.h + +gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h + $(CXX) -c -O $(srcdir)/gc_cpp.cc + +c++: gc_cpp.o $(srcdir)/gc_cpp.h + $(AR) ru gc.a gc_cpp.o + $(RANLIB) gc.a + cp $(srcdir)/gc_cpp.h include/gc_cpp.h + +mach_dep.o: $(srcdir)/mach_dep.c + $(CC) -o mach_dep.o -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + +mark_rts.o: $(srcdir)/mark_rts.c + $(CC) -o mark_rts.o -c $(CFLAGS) $(srcdir)/mark_rts.c + +cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c $(srcdir)/cord/cordbscs.c -o cord/cordbscs.o + +cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c $(srcdir)/cord/cordxtra.c -o cord/cordxtra.o + +cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c $(srcdir)/cord/cordprnt.c -o cord/cordprnt.o + +cord/cordtest.exe: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a + $(CC) $(CFLAGS) -o cord/cordtest.exe $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a + +cord/de.exe: $(srcdir)/cord/de.c $(srcdir)/cord/cordbscs.o $(srcdir)/cord/cordxtra.o gc.a + $(CC) $(CFLAGS) -o cord/de.exe $(srcdir)/cord/de.c $(srcdir)/cord/cordbscs.o $(srcdir)/cord/cordxtra.o gc.a $(CURSES) + +clean: + rm -f gc.a test.o gctest.exe output-local output-diff $(OBJS) \ + setjmp_test mon.out gmon.out a.out core \ + $(CORD_OBJS) cord/cordtest.exe cord/de.exe + -rm -f *~ + +gctest.exe: test.o gc.a + $(CC) $(CFLAGS) -o gctest.exe test.o gc.a + +# If an optimized setjmp_test generates a segmentation fault, +# odds are your compiler is broken. Gctest may still work. +# Try compiling setjmp_t.c unoptimized. +setjmp_test.exe: $(srcdir)/setjmp_t.c $(srcdir)/gc.h + $(CC) $(CFLAGS) -o setjmp_test.exe $(srcdir)/setjmp_t.c + +test: setjmp_test.exe gctest.exe + ./setjmp_test + ./gctest + make cord/cordtest.exe + cord/cordtest diff --git a/gc/MacOS.c b/gc/MacOS.c new file mode 100644 index 0000000..cc12cd1 --- /dev/null +++ b/gc/MacOS.c @@ -0,0 +1,154 @@ +/* + MacOS.c + + Some routines for the Macintosh OS port of the Hans-J. Boehm, Alan J. Demers + garbage collector. + + <Revision History> + + 11/22/94 pcb StripAddress the temporary memory handle for 24-bit mode. + 11/30/94 pcb Tracking all memory usage so we can deallocate it all at once. + 02/10/96 pcb Added routine to perform a final collection when +unloading shared library. + + by Patrick C. Beard. + */ +/* Boehm, February 15, 1996 2:55 pm PST */ + +#include <Resources.h> +#include <Memory.h> +#include <LowMem.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gc.h" +#include "gc_priv.h" + +// use 'CODE' resource 0 to get exact location of the beginning of global space. + +typedef struct { + unsigned long aboveA5; + unsigned long belowA5; + unsigned long JTSize; + unsigned long JTOffset; +} *CodeZeroPtr, **CodeZeroHandle; + +void* GC_MacGetDataStart() +{ + CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0); + if (code0) { + long belowA5Size = (**code0).belowA5; + ReleaseResource((Handle)code0); + return (LMGetCurrentA5() - belowA5Size); + } + fprintf(stderr, "Couldn't load the jump table."); + exit(-1); + return 0; +} + +/* track the use of temporary memory so it can be freed all at once. */ + +typedef struct TemporaryMemoryBlock TemporaryMemoryBlock, **TemporaryMemoryHandle; + +struct TemporaryMemoryBlock { + TemporaryMemoryHandle nextBlock; + char data[]; +}; + +static TemporaryMemoryHandle theTemporaryMemory = NULL; +static Boolean firstTime = true; + +void GC_MacFreeTemporaryMemory(void); + +Ptr GC_MacTemporaryNewPtr(size_t size, Boolean clearMemory) +{ + static Boolean firstTime = true; + OSErr result; + TemporaryMemoryHandle tempMemBlock; + Ptr tempPtr = nil; + + tempMemBlock = (TemporaryMemoryHandle)TempNewHandle(size + sizeof(TemporaryMemoryBlock), &result); + if (tempMemBlock && result == noErr) { + HLockHi((Handle)tempMemBlock); + tempPtr = (**tempMemBlock).data; + if (clearMemory) memset(tempPtr, 0, size); + tempPtr = StripAddress(tempPtr); + + // keep track of the allocated blocks. + (**tempMemBlock).nextBlock = theTemporaryMemory; + theTemporaryMemory = tempMemBlock; + } + +# if !defined(SHARED_LIBRARY_BUILD) + // install an exit routine to clean up the memory used at the end. + if (firstTime) { + atexit(&GC_MacFreeTemporaryMemory); + firstTime = false; + } +# endif + + return tempPtr; +} + +extern word GC_fo_entries; + +static void perform_final_collection() +{ + unsigned i; + word last_fo_entries = 0; + + /* adjust the stack bottom, because CFM calls us from another stack + location. */ + GC_stackbottom = (ptr_t)&i; + + /* try to collect and finalize everything in sight */ + for (i = 0; i < 2 || GC_fo_entries < last_fo_entries; i++) { + last_fo_entries = GC_fo_entries; + GC_gcollect(); + } +} + + +void GC_MacFreeTemporaryMemory() +{ +# if defined(SHARED_LIBRARY_BUILD) + /* if possible, collect all memory, and invoke all finalizers. */ + perform_final_collection(); +# endif + + if (theTemporaryMemory != NULL) { + long totalMemoryUsed = 0; + TemporaryMemoryHandle tempMemBlock = theTemporaryMemory; + while (tempMemBlock != NULL) { + TemporaryMemoryHandle nextBlock = (**tempMemBlock).nextBlock; + totalMemoryUsed += GetHandleSize((Handle)tempMemBlock); + DisposeHandle((Handle)tempMemBlock); + tempMemBlock = nextBlock; + } + theTemporaryMemory = NULL; + +# if !defined(SILENT) && !defined(SHARED_LIBRARY_BUILD) + fprintf(stdout, "[total memory used: %ld bytes.]\n", + totalMemoryUsed); + fprintf(stdout, "[total collections: %ld.]\n", GC_gc_no); +# endif + } +} + +#if __option(far_data) + + void* GC_MacGetDataEnd() + { + CodeZeroHandle code0 = (CodeZeroHandle)GetResource('CODE', 0); + if (code0) { + long aboveA5Size = (**code0).aboveA5; + ReleaseResource((Handle)code0); + return (LMGetCurrentA5() + aboveA5Size); + } + fprintf(stderr, "Couldn't load the jump table."); + exit(-1); + return 0; + } + +#endif /* __option(far_data) */ diff --git a/gc/MacProjects.sit.hqx b/gc/MacProjects.sit.hqx new file mode 100644 index 0000000..99dff88 --- /dev/null +++ b/gc/MacProjects.sit.hqx @@ -0,0 +1,886 @@ +(This file must be converted with BinHex 4.0) + +:$deKBe"bEfTPBh4c,R0TG!"6594%8dP8)3#3"&)e!!!"4UiT8dP8)3!(!!"50A* + +-BA8#ZJ#3!aB"#3d0#'GM,MBi5bkjBf038%-ZZ3#3%)Zi!*!8"@`!N!6rN!4069" + +568e$3`%!UbqAD+X`19S!!!Ba!!!,*J!!!F%!!!-PfTmj1`#3"PET)d31)LTH6H4 + +#*AqG5b5HI*)QjY$IIb00%ReTJSi6rG$jG(bZ,"Rc,9Umf[IRj)6FZ-j`GfGR)#! + +m-#qLqB#cj'G%46qffB3q8AppLXKc+P&*il4FMJMq3N32r[U,(PlSNdrQm-J(4!p + +jK)NHmKJSHY!,&chS$4)pk%8mL3I)B0'$AU6S3'q)k%%[5[5J&ffa#68)0ZM&#T! + +!*fHC-2dFZ3i83[Vr[4Xh'+DNQrm'J)rrpqe%ST`,FeVi6b,*qHH")4eQc28NFMN + +ZT*m,L"Y%-`pdAk6RLHDaeVV0a,,@P(4UUK66rUM'8bf91llS("lTh81)MBQ+4*q + +rfHENEhD)Ke#3!09'M%bL[P1+G88fa$3e)5Gpf0kARpBf*6eIH*0`ZBHR%ii"PbN + ++D&*)688M)Sm$Bm[cCdDjh2YIjmAc`(TVpi*Vka((A*&Yl@'LTSH1M*AMP#,2[A$ + +(FHA@S"dL4dER#3b!EfBYem(C9P5iGH"a-bb-AL(F"bb-AL,F6)%a9pJUL,(hf%B + +TeQb["X5ib4DQXV!-fa6&mZf&3,(C&UDd-((SpeMBEIB`8Zc,BcZR3A5'X+jYj$' + +6)6HVV+R[!`#3!`X!(E@*MFQ%R4d"))`m[3JM[c)bBS54Tj'M(AP+MK&f%VD5SdG + +SANFB@3Rqc$Am83(+)`"G(D%A'9!bBQ6!b)b4Sq3SH8D1NDGNX$)bBi54!51--$* + +Kj0L!M"KKK"dC--,)-h+-6#KKC-$)-F)NamL!!Z06#X!!b&%bBUHp8RcN'%%6!b, + +i!!kV"`"DLHFaK*!!"Ym4K,,2i2X4c[,`c5!GIPf!ZcNi'8'VfJFpSfdpq+CY$8j + +-V'f-DZr2[36#1(ael5hmfT@1cSU66D5pqDSA89pdTP-`Z[jj6T&!PmZBFZjal"& + +5iG6#blE$+&kLh#QZ118&(0T1J(hZ,9)5MJ9ic*qPI!ac'RJ96QMZjSbkMq()Ui6 + +B+f,,#'N1icbM4N"aaBr1`3Z9U'8RY'XAiVXFKp#&k2D5Be%VCdh4%,+2QS'b"Q2 + +%0PNT4rE#%kTUFqYDM56bVjfe!p8MqmL)1VmjVkJY`U[*$&*L3AMSpB@LCQ*U&l% + +T+3890rL,V9klFN*4@f0UTf8Z&&afN!"4GC6G8p3fN9$4+4[-@DAeK%lej"@eAAL + +eU@&4[Tm28%mqqUkS(F+VDa#lB&'rlRAllRP&l460Qc,)MHR$jMh@$8Y4Xc'e`cd + +ZE2AUUiH+fK96feb$epq&'RAQeLG&lCDjmP+"Kr8k9#qp'eI8RPf[6R$dS+$UcqI + +ELYSV[*ETFL&j[@lr803qd9I2A#bi4Vei3*d[+@Urk*!!&abe0HTVm%44"i4A6JN + +c(2I!kjRl6a9e813DK"A6p(LjRZZGaGH+1L5SiBT[(6ekd2*ILMSXU(l)#m3QMDB + +V+QTG!r*NG#RQai#DNh4,l0&!Ie`dYi98Y1%1A$5hKP4,`d9cHdKP'LkD@q4hYC* + +%dfdLeCCNN@i9UIBNLh5l5(8N68qhM&4R`d9cfdKP'bkD@dHU+qe&XRfNZSqc10j + +#8Me*&ZNfNZT0hSYd+dP&ri-FGM6G6P,p5D,rPNT0`dQLk5+6'NLb5"HDe'$L)Pe + +X8N2bj-Z'$r$6-$NZjLGC)1lB-"jQSff@[ak%LJ[rI#%p2ddAGREN(@"V+,S6CI! + +I!!!0$3KRBbj38%-ZZ@0M8&"$,VN!N"#$BJ#3%4B!!!d'!*!%rj!%68e38Ne33d- + +"!+X[PfqV-$P*!!!'-3!!&UB!!!(&!!!&C80(jji!N!BMM#0%$L)UANhN3L9rV@9 + +B`f#c2p$XpAVVCc-[`k20Y5bJ+CTHPScj`Z'!lmr*#EPaRH(ZcR!J!!rqMKG"q)# + +cj'G%46qffB3q8Aqp4R6FA83PM6`KUjaYD&IlZ@jDrY"pk[b&AZrdH*kFbb9PM*S + +`4Kh$c8Lf0bVe+Y`Q$amM6mc%*C1(jF&1bFSdGIlLpc*04b#X&D8[&6R%+-#6HhJ + +kX"#A+Bp6%6RGkB&kM%'jh$ZLmam[1Irq,r82rGM"5H4bh1ZB+b"Z%&-pD)5CL9( + +AP(4UUK6$!(lkH+UPFXFARF-MIHHMXf!5Nd%SZYRQj'pfL)G3N!$94X#(q25G8U` + +VXL'QU3Njk8[phV2@0Q92J#d6rA2N1["[!%c(M4X-8p,0IcYJf2lRBmD2c)*RQEF + +68m'9jqq*MjHTji&GqDp$kh501r9fqVPJe4iQDRS)L!)ELqiX08i#@40jpP1+F@p + +iC&))L)Qq4Bk-cK-i*h`cDlN1cMBUbZA3+beKhX*-&UD`X%ME%F91fHB3BaCC''Y + +KNba-C@(,"-40Yl"l,#c8`YCDf%#"XGD%F4m3'*i'k"iah[Ddam+k"Xd3eV@02'B + +bj'D90I9p!!!-q)[jAU2HhQ[NiCQC&f(Ne`JR!hlN1''4Sjc`)hcL5IK+f(@8(q& + +(1&Nj2XreTBI[M!0dGB4'MK01#CFF2c,JK"*1MNZ1(q&(11@5ii5EKimF*ja``Np + +#bA(#bBL6BpQ6jq5imT-m2mQ!dq2N'H&2RT2M%Nii'6$J,PF!#N#jGS3IS9Uba%G + +'A-)*8[#%!j-9'#r3@EpUPQ9+NL6$ldj*kVS6INIK@`*q'q$hGRJCPb,`pUJm(fQ + +3!#mGrdQqe$Nm22hkJ2cerNp"i3$m4Z62S5YA40V([V`MbHF@)QPT2IN@3@$ceHm + +I&dT3GqF9K,'&&8[6LKMTbQ6@-*%bJE#4RM,b'FA*'VC5`0BBdTa"@aNXM#)mU'" + +N@d@XSIKMMiMh#RbbLSjLT49GG9"F84)Q8QfN&![N1hK"A'V5F,,dJIF@+`iNJEb + +H-(5Nar84j!"*Q54MH+j&08dYQc,(ipT9I+aFqIQc-XP313&803UUPPD4*+UAIlj + +$U+jMAP1QUSfEYV2Qp4HKfZ#TYQTCT)hEaCbp+ZXH0"m5USfHDV1HbL4cCT@41rr + +5+d+eL4&+'hR90)iLRp$LYcm)e5McQN@UMR#&$kKqr%eHU-DBejbUCC-k+P4N5r% + +Iha+Uc5aj)kVfm*'ej*8Dali5ULfHDLah-l$Zfer1#G9@6l8TTf*r,RKTZ2#Q8'h + +MA2&i%MYq(0aCicHKfPlfDYLeJ3*FFEG3l@"HmfJbqFrdHU&IU+jRHE95BmQFkJF + +29)qp)93hX!aCGLfYP0!jSEU4HF9)-e8M9rADGfC4U(BbVVC66+8XR2Hj2RAmGk' + +kLDNk8`@p0[6F"hrG,e3h`kmm(BhDMQjBm@`ejDH1pG)YbUXYM'Y'5aD`-H(VPZ) + +,*i6A,Nqe)D1Y'5@UV@HM3VAE)a3$3MT+9jAGa)HI#%*E@9ie+jmf-PA9dY#66`Z + +[fkMA!l&$eZ3)bP996crcal6`ZRdT$9NG0S#+V([`rRZ&eae,A%dMGB2V4H%9YPL + +LfZ3B194,NC[ik!QKZSYlaE"deVc1$3[9(XVeFJIG0T,9**@'AVXJZ2Db$%'!,$a + +e+d2+8SES`Z&RD1(C`m,VlM*Aj)cP#M@ZlJI#Djp(U28`fl)VL9dKY+IXeFM!HRJ + +MVc0#YCpj6@!,M0VrHYh,CMQN!FBjl1ZVEPhjaCK)``"6,6JiU@@ekMjdmEEPI@M + +3DpXKj3pi+f`LFFpIUPrF058)N4X)f4ZQ*P5c1[&!pGhC4i@Ue2BCE"bRL&haLRk + +Thb#ZUK&ZK-Kc9k4Z-[QKhdaf&1KhN!#*#IdZ-XfJhdPQ)I6l#![SYjD'HXp$hdA + +f$1LhNlN-r4DbV8$I8iS[RSEqj#URqY@$9b3dJG1XG))%khUHJMX,Vh896Z%"I%B + +PFK1MejpP2[@,$LpbTe[Q%h#[hhai0BBHF+r-MrTeL9G6k!!IKHa1rmf2qMf,9c6 + +d)%I[5Hq$1hVVq60(`H@-9fb&cfkb$BBDc1-Ck@@#jrVH%0cXH$@cIK[C#F&2Q9X + +[qpl(HTpEQ9F`KqVA3&iYS3Pl6#ARpIXMVpCP6[+ma`PkbJPkbJPkbJPkbJPkbJP + +kbJPkbJPkbJPk1MHKTlbJTlbJpqGlF2RNe4CD`1XDTfUZEYjDHE@[F0T$,KbK"Vc + +mA!9AAPiGS3Qjm[HQi+l-LraVj'p1i3&mcNKce1@eZ4pFX(PY@1(66rD18)Im"eF + +YAJ1K#AYcK92peXpVBfM#AZAIKi*r&r$U$"h)dkhp2[JI!kp0S3GjhdZZV))A!43 + +jH4kk(TLQKF4pTXhHI!ITRb%hcX3KfeN#**1EI54a"'@Z8(9Dm%D@b"Y#qhm!N!- + +0!!PRBfaTBLda,VPM8&"$,VN!N"#ah3#3%!9X!!!I``#3"2q3"&"56dT,38K-!3# + +TY1))Uc!eD!!!@F-!N!B563#3"2$I!*!)22J1`2KbNQaPEr+hGEX``Jk!Vpa0&eT + +RDl*eSGZ&%EEAc@iGG+hAYBDRapHZd6ETQH'lV2AbpMVJ4lN,ck0G4lMb)fcKAQi + +*AeLhm1)VRfPGM,"Zi8pBG1%a3VYZi@m,@rM#2'iAfhjHacE,K"[bJGYB,ZcNP&# + +"$cqJ[fRG`SmXR'aMC-H6r-)AXTaNHE+Fj"HkN!"0"R[G!H4jITB&`!(!dKX"PZ# + +Z+PX+S(dCS&YGZI3,cN3L+P4H)V5R@D3p,54$JD"3'!j')mhRcl%mUJ)9e2PVUaF + +j[6lNX)ll!4,jajb6UrZK!hSTX[caD`$ZIHl,pdeVm&EaLeKG-YjQB6AKT)84pF, + +kB$+55%ID`b-4QF0T19ckfSl,d['15$X-4cTr0"2!dIR5%1j[S4JQa0,J4lT!pkc + +"EjcQ2ZmmNDF36,1DH)X!8($N3ihbR+mcX1GC!E!0fi)+ra)rCUL`#HU&V9)ke`6 + +IhTB!b&RK%B!&4fA8Ecr8+8IBcr)4Z8L+$bmVaA0$-Lr)$3+SMf0Xkh!%1L(hiM$ + +H56i!P'Q(V3ZXrmCRE,f[6f'0N!"Z$E6%fl(AqCL20Ka-#kRdjh`qA&CRACe[!5i + ++PSiKjh)6PJM4H$#5%&U%HF#GqF0F$MM6fH)T68dFSQ!hQ*["e3hGME'TS#e`Fmq + +Sl`'0qRTZMfEcM@b8M`(hV,a,kqB4N8iZ[4Sh5b!9ddQpT9YP#5UK!NX`BDbr,"E + +!TME)X#08Bm,*$)fP2Ci@G1bTGUbETe@@q%4QL60h[2d5)BQGX-U5,*6)q)99'NX + +bP3a1pJZTH#BC&"!P%4'5XP`!Fm82LidDE@#h&eejC#m'cSQd"k1C&S(CD`*"Va" + +S%C+TmmkE6aJ*6S3kTd8)4GS&PNjQ"#DY1419T&!JQT+cV-0*5@'9$$5+K-58Y"% + +N8Ea'&)q3!*!!UeBZ'qd'!&14D",LQVJ'$qTI1DUU3$%0cAD!e9HMkl`KaGAASBj + +TJ#pMhSb5Rq0c+LJ3l3LJkD2dcrJM2Q%3Kh&mZL-JR(&m+L$L-)j29b,%B4br8)j + +X!Y$j4ZUh`)[eI!A!R(d!4AHG`LH[d[f@re6*b2mAI`)H5F0aI+2XYq2iC)+N`6M + +qC$b5"Z2ij,N%KHI*24K!$k@Plm*Hm'Rd8-bci0h@*rK6m%JDM[-[aZ1Nhq+IKNH + +UJA&mE-V&'KM(2a129!2Mq2,5(2qIrSHmNfTSR2rTH+3D'XHRfL81irM8FE,Ep4r + +eTUeM[5Ra8bilkJJ6f!)lF0e(0'p*Cke+2Nq9ccEjh#UIZq6c&[RmM(3ZV*!!cL0 + +k&5l"Jp4$Ilc)-m$9BDMqeV0m$l6LhM(EAX9A,10lG,aR)2GNb6Sm29&b0@CfmMd + +&Mr!pHLh'hX&p"qiPVV#h)jIcaN(YAHVY!-im,lH&lp&Fc$pX!KD$+,qKqbMQh", + +@BjDAX[M-KFF0&bH!le%r'GC@E`LVXP9mKXdeG)3QcED[U18Vq4jY2c-fD8XFl$a + +Jb0pEdXPRCYXVR!e1c(f%qF`GKAUQcPT3T6E-YjCF2GYHhq#[aqa0'*p@XJl4r*8 + +qM(Fa(e1(MAb2DUZDVTq-SD2mJ+kFAj*ldAQmX-KFQf"C5i,E1fA&P2jHj`!8*c4 + +Cbq,eU+LUqmriLrQ-H$8"RJ(GXC,YKXYCKk(M!EcN!3MV-HG3b@DB@MEAd"P5,9[ + +2CjDYplkH1ckr$1D5aNf'jH[,p0ehXaPCKe@(eI0#11SC',UQT)X9K3qD(G8hK#c + +C@GQUfADhU*AQPE#2X"A&i-9KaAUdDe$"bpQU)@mfJNfL,U61YQ4RBFiKFac+[hC + +Y@49Fi(Ye4UjKII9Fl[b`UM[(Ca+6ZhF[@mq`0Seer)R3*#Y$$IcK`pPc%EI6FKZ + +I`IV"'%bLZK'Mdl!5jqQ+3J!feU'k*f(FZf(EGY@@N!!CGAmMqd9@CrDD68d'jf( + +3TlQV6AYhAEJlGh4$epjV3bSqBiDXKA!BPjeTVUYp1pI,DPfESAK1"2eSD[B-elh + +H#"KCEIFl0K-Um0E-CFr[,$HC6Hhc`fDr-eb-HmN5*`iSE-8)!#TL+mfKpUV"jrc + +$X6fMXIlRYZ5'5$I94YXX-&C(`""L$Dkf)VmVe*%)GZr'mh(#3i3EqlYKNKblRf* + +'9fi`h"aV43`ejERI0DPfA"MDB``XX)HHa#bYS3h1c!hCcPlQ0+mDh0Yr`mEU8Hk + +YrAmUXCIMj8SFBkA%6iNVCjRI%C(IMj&E3@l3G[C&a#hGId-rBQbXrT)c0e6q'2p + +eC)89`[fJmPd62,qrh"5fBCA-$%rb1d1R5hbj`ddQ1G,60%Q1l'T#EqB1)110@)h + +%i!95M+ekEiM0HfqSHM1k9UQY&%V$jTQPB&VZFVm*4FmG"[Acbff$#qbZ,a3IKUr + +B"VZ2A1J-[B%elK$paa&k8Z63JaakNVNdL$c1fP%+A`QGIJ'bm6iH0ZklkX(0S"E + +8jP*3Mb,[3pbE@&fLD'2RS@ZY1`pG"kj1X1j#2R9*X*QX*TAMbYcVef*YX2)T6FA + +Q@D$Hf'AE5@VBGSP+2*elSqN#9T4Gc"`I)"SMr!P3K8hPL)Se--@E+!*#j8qBAdA + +F)f`H'*JMT!TSH@V*`'V2IZI1K@DpeEljYRXA2YJ9eU,IcfjLaVQJjXS%LTUELM' + +UNU1Q*M@HTVX(FV[-AA`QqadqFr3i9[JU81PlSB$r%d$A3iqhZfXV+KG!GjBeeU( + +[-cfI+9deX0(XqqDqeeCrEqGcqm6iUPf$i$#AQd`B@p0rSjJ6NR2d'hX'fX5-"MQ + +MU,pRS%(-F-NCDZeUk[$*BA*h$2XG9RaZHj-D6bq3!1YJC6AD61@QEFZ@lXi09,[ + +#3r`40LMRE"V0'C!!FecYKJh1Q(D[`hN%90BLbX@@Y!c8C8j3QmY!ApD)[GhVGTJ + +**CcApF6MTA!ZjkemqUrh9AKG,PI[cVeVI+q#h6`$QIm$kKcXmZ"@c&ph+[pbaRf + ++-2[6I1-)JqV1YQR9UpZ-&Cd9Uc'6i5P6JCdV6"8c-TKV%$1eQ*@af2(L22GJCe" + +VaTDFcfaEffcXh1Pef-$Pm$Vic)0VQmqbL$(+mRVQJpGcr8kVcZZakIJ-9F5"VJ2 + +A)XVacTfpDfd&ZhSY"9l2XleH6rpD3Epa6E1D10FlQJjH!G34SPGS&qM3*fC3Pe2 + +L`2L%lVY,CV!*T39qcpXH[fHHVQRU'%UAhk2&Qk`VKaD[,i2ZHk`cX2[6K&iQRrQ + +lbPXmS@QX)1Y!&RH`da"Y"8BfPYDc4GPC#3lV4AhlG+E(2&HTGaMM!VD)&65CaPL + +Dr4lQB&J09`k9kE(,mhf[0f[T[[2#[mfpH2-6*6k4bk,U5Z`kcd%Ia$UcfEZ2Z!G + +1&'%PEF2B1aKl$'0hBH`R',X1BjX`pP1-h6AD-aHa8TJD0Z"T@[KdIJ$5L*0!R+1 + +)NmCi#mDEj(J5i`fS4KaV[49[Y[ASjjGJCfSIkdaR)f+)e-#cLpMMH4iTJQFE+B$ + +RFiN4RXfXNFpBZGXAc[3QM,G2Yh*CMh@3!(q8lFE6#ID-P'YZ"AefKT9M99N2Re% + +Z5UJ[cKd0UjR$Y@%N5eQr[bVdDANH1X3[2[#XjcJ0%Se1!jKa'U#f[M%BE`p&`TC + +@-mfEF*1J""c`J'Sc4b0!`0Q1cH9X!e(3aCl!)H`k4qIhpfYS1)*',+EMMLJR'JM + +*XAVRp4,L3*6EFHJLENI+bThcfZ@BBX$BV8U1Sr-@+@iljX&F'M+D6*J-'5#(%1k + +[1&EhlT'("@L3!%(&RA-a6V0,2#9X9%3D8*&8fT'k`V(k5V),NCZX$kh*MY@GDYV + +4Y-8%c[bAlh!l-U6&69c*e@N4Mj-C)C2d+XbiMLZjUSJ3--Aq8HQ-$[R0RcMaPa8 + +e&lLqlpUj[TGS[iMVqri'VZr9AUl[KhZi[J-YA0r"GUl[d&eFhq'YA0rr0h*pEml + +RqYlHa2Ap"212)[Ba!pGh2-6e$Gc+p3dqbr80[FMe`hbZAjA&I4IA2aN0'##DQ-I + +F0B%8$M1bX*!!6V&dUi!$KD&N2-DNDAZFBic&F2BrKF2r6-!j%"D+4)8c'q,aD,f + +3!-3j51B9SJP@RdlLA(j+(8X++A@L25E3BD9ki@,HV9l@i1F0$6KDbP$RC(bL'2* + +%ikP8)(QCZL15MXe30%"dDAVbI)DMURqBCV&i5b4dfDrbrk!LN!!@@#SGL#9B+*j + +N3JH#Y3HLV#@5r"fhhq@IS5Jp9LM&BLQF6+PSMTk2cbS%9c)KQ@5a90K#Sf4N5PN + +S5M[3da4hiQK)k+XiA(ND$YpSYSe-m)LIZ,6N5rL%!p$M"e)Z2G@JJJ8FXU,((EM + +pQ)@$C4*&(*ZN6`SqKSGP)q02Q+F@[iqA@RaFJFBHbCM4qfMF%h!%89`D('LN6e` + +k'KDkIh4i5)XM8r4*4)JcM9hKZ+)%Kcj2Rl4%aj+pAcSALTmN,qQmF&6[3Z`$k*0 + +%H%M18RJEF-b22R&0qM&+6,@P[&-a!BIik*1U!BGKe64B611lY)`iBNHI9"S+Ab9 + +l)JjKd5HT3V25,H+!P%`9Z`rkT%9kNCS1THY!pHQ6Q&%@$8)T99L%Sfhd5H*hI$J + +64C28Y,C`Djl#m$6b!XGfTmrR*X8$d@L`Y6QkdK+%4i(E8[b59GP&,"cqQPC3ih4 + +MlA''N6k&X1iVfl4IfC%6%hNG3kaD8[4Nmd+LGcpXR+[Xb-XNFZZYEkLS`Q4G+Yd + +5L413!'S-T`$1NR'U9P55`+R)+U%aM8!K9-"b-+[Xk$GR5FTkh)hN*rJB5@-L'EP + +%j(6IK+GdbSlH-e9"XT!!TkM$335*3-%BFqd`miD+#P4)M`VKJ,5STAS-5DFJ,A9 + +lRF6mdQ"V)#Q+K-c,[YUNl&M9XNEZ@PkXmY(k8'eCj+P3G[5T%69*)e+cY5@CqV" + +#$%SP0969B)9`fR3N*L#-jAfF#50kqURL8%pU-)M3+FmipZBILqkTH!E9YJip)aj + +%`mKhi"GMeDhkeqSZq1IU*VIi[,SeRcM3"dM$M['C$j!!BhcZ!m11mCN2&2k,$aK + +qi32[Hr5%Rh[d,hX-I&T(k6&F2UIBBc4(!m'9d93k(d+2NBr*-djj`D*SpBJAZ,f + +9j!86F'3iZ$+9LDAqShqJf[jh,cLPbr2V[SPKZ8BUA*j'UT'@jR"M,2UIAFerUC* + +hbU&Hqqk24KaUB492qKV`$C4!&+Z"V#$rQ"GJ24rmKPrCa6X4KAZ0c$d@5+lmTal + +hVejS(qNI[*91V#iSP&p#b,2@2paR1A6E52mJe6FBBMJ1dGJL*2+9p3qIhj!![Bp + +M('C8fB"h)XK)5,I&%TpfThIZ`BHa&(9Vm2+9kL#QA,kQIZdYiIaLYrARRVV2f2q + +YNG[k'UGr%8DeBN-EK0EmEAlarTd(p5,rIHIa&j&hIpETLXk#R@jbC@-b,9jkj$[ + +SG20dc3jaep#MG,*Rm*9,kClGd#jFfLM2Qq@TmibVrRcNcU2@95h1CX5Efl"&%5r + +8mURGV@U5ZdHGS,k4EYRemG4[EPCrFjZ4PqYQYFV$Li`LB4cI%5Ak4CIabTc4cV5 + +Z`5pfTSPdXM(B'Xb,d*RQlCVl-6rbfNK(iUpddhemB9))4J14@"k%hM42efh'efl + +%*i192U1qBE',qSa81Y2F(%qfjbIV-mbRlM2Dk!QiiGN-X@CeBXhQjHJG2R%#l)P + +%*m$r!"'46R)DGS+2k[XNTp(qiGGq@r81$FI)IYZ`[)lZM!cTba)YbQKh2VHq(T' + +iYATPahXMf583L9i#-b!5'SA3JP$LMk5FV"eL5P&e,)!2AM(fqq[&rAqqJEX3ZJ0 + +4GUAcq1#I[$MlrpXrj3jb$ZiY+2BkkdRM@qKR3r"mcb,mia%m2lM89dZ[Vqh!-,f + +QqNbpVjjZ29qJCq04M`2d!b+N'UT5MqGLqX832%q[Aej$mA2Gr%)2D,J,T!VQVUK + +`%6jhAB9V+HAI4,rjJHFl+Pb,m4eQEZZ5@KrPp5aF@N9GqC2+ql1S&YkPdTmG6Gr + +!qEV`09U+&4c&223NLQNk-DpALZNdR1mDqVXNM'QAB`crlBKL%mp(M*G"*FCZ`&J + +DZ&cZG*Ki-f,J@mmLMhX`*R29E-FB[Qe,XDNr4DlPFZc[1GrDKlkqQYkKeBBaYUl + +YEqK(@E3aM+N[HKM14ThU%2X*Hb(-`McNHXhpB"3j2BDaPJB6I!Ne%&qEaD`r`V` + +YU-G"k"3ar)MaKKaEKl'$NQC6hd1-Lq4B$Q0G-XB+e-BRajCJ,+'*V3bd4NrqAp, + +B[bJT[kddmXG*R(e#AIa5)9RRT[cr!`!!$3!*Cf0XD@)Y-LkjBe"33bkj!*!3qL) + +!N"!0"J!!,h3!N!6rN!438Np+5d&)6!%!UE6L#+X`0A!!!#*k!*!'$d%!N!43[J# + +3#1j"$F$iCXbcEQ9ffFS2dS@*jbZl63NYVcACZY$0##1XPDZ$V[@ke[$dmVQ6K5h + +FYGEmE+(Rmc@246PGf0D9hF)@VNAi`VhS`KGM(GQA+lmmdfiI)f`c`Tq`63P23V[ + +Y`VEH`KHqX)9f(@(E*!Zrf-)@IZi)AhKXi3[E,M3j*432"&!HrHaD@&$M#f(,qq3 + +@XL1hN!$"3Rk6AcKCb%+1%di@J&@""TeG+a&(42abSQ*m9@@VL(4[%29TUPEGj%S + +NfN09'd1a&"q0T8,*F(-`0#85E)pZZ-eZrEB+Z[80G6A,A6ir2'5jYd$i*mlPdrI + +-@8-1XA6I6r6dUG[h&cAjUSAPI(dbhQEPDb0*+mqX6fN-*U1*9$3@'8GN$c0%(%0 + +GelfTH&Fd4Q0)jLrR%MNc2aM&pcf8d``Y,Ak!B(cHb*GQH1E2Phb'JLQq0Yi5)P* + +IZ&DMccNrDX`mDiN1BLbSE&MC!)B+3p!!(FM4Z3"pmf##5,64Fd39&fA9Eck6N4( + +q-Kr+TK`qGQ`-&dGPAb51%'Q'J"dB3bK$iZYMHPIm%$'QJ`j8f2l6cq5j@TmTYD& + +8Dh0,2)CCjkGqG*&J+Y5CqU@IDmIQUUrh9q!`X*4GG$59b(1#DBYLrXT3Hc`B6B4 + +D3NZ)Zr'(SNLFq4ETPX+0#01J@-c9Mci&E"ETe"lZK'B2D682F5pVpcl#6cM0`cF + +VIh2RdI%LA6N'$6l@jXi1I@kfp+LX3395@i-*Bq1p(FdBDS-m*N)0#&FB@QXXRJV + +TqHr&d$F[UDca!YiDjchaf-C3%T1`bTUFNM26%1V@@T1GbH#dKP"R2*d-KU#5L)D + +5FVQ)&NXr0"XEY)Prh,6j`NN!Fk+aB(Zk*F3lDTZ$[P"c5bMC1Arq8UD4i#5T15f + +KF$3@iP2*G)M2RB8&#LRFh0iTXfaMT'5S@aDD8))aK6DZ*"9[2BV(P+51c4hG,L+ + +c53S*k44Xa8Acmd49U9R$Xk-p6,4P'e,Rh4bZH3"e6"(G$Pjab5Ikh&MNk*3JKBH + +am`[rd,p4KJ)IdrpGAkQ!SYrdArSB+K6p(4q-kaYR%DeiK@MHTTrT+airpFpf(!c + +C6D6hMrH[fSGq[SpSi@NLdj2ApC8!q05rrM0pH5A%p,FGr*AqP!RpYPrTjl,kIr) + +Mrc0p)kiXJcl9Cb(1%'6hP`BRQ0MP'EU4U`lF@CCrSLp0(%#3!"HAp98B52*lSGq + +&ZrfkrM3CD5@kEp'%2R+m!*ldPFM#f(9p0R-`C#rdT5&)cLr`#Kk#rMULrlIXZ[j + +d'6P$Y0N+!(Y!54rDdc&h'$"brDYqB3l4$[hhr$0$4PE$2eXNb2ieb2fErJLM)1T + +RZCa*(rQIH68r2Xk[*I+#iKreEj!!r52r-kc1XRmYjSpI3ai@B(RaKIqI,BSqG$# + +E'MkH69X[ckB'iJEe$Qi`RhhAFB-&cq&lKKZFKRc"-D9m50)#'Z6Fp%2+jFLffS0 + +N5Tj%4@C5"GI&cC(ZFcD,h$e838lFZmM*m-eX'F$dP%A,,mqff[SF8$&N-KPiM91 + +9NF2XSa0J@f1fH(J8"hGPCVYkTSRLJ,V55r6R486P'%J,"U5PdFrVi(p*UM20Z#1 + +AjGIGE[0r"EdLeqdcjp[mNSplX,Y)hCYJ5aj0I@@G*jb-Gm65lHf-'iiR1d+aG!I + +M4Q-YACfKpTEfZ,40CpQLY-XkZ5B+lNFp6BS(cVppFXHLm)JE3biI%jRZ4TD29iR + +SY!R1P$QEBbjeBD*lqi'1GccMbIje'bEC1H@a56dI1a@*I@9pEqBF-qYcdaaAM`b + +5FjP9B(QLVT*e4Aa$'kXN*T*FX[j[jrbLXcJ8Me@X&Eh%AL-JTT!!Gd4B3#S&rjI + +6(0UBDSje*M'BT4+G-9BhC9*@-5jcH$[1@!XpJKl'$ZGDCHXmRb03ICB4reapCC! + +!(Mqj("6&rGSNfp+B@FQGKfZV'cfXb6ZLR8&V%2h"l5[mJ8hjJPR%eT0&kPUA"r- + +MPcHq*D-)FI[,GTp4[[$$5jiqJ&BGP+G#UkjaI6!H#dFM9NbNa28pDebXI1(,,(N + +ED'bUV!CChjPULFDCN!"U8NG00mXke@ZV@1Ge4VY$ke-3#PpeT"PAmJT`"+9)V,N + +pTl6IHLkVI,'RZ6PAIkpR2HXM[+GCRdK'0dVZpqGr6kpmXC'CT5KCd3'NL33K%LA + +eT(2pQ21Q5[3dR+GDX116UUkC9$)S5UXm2KGcINq`Y6NTP421bhiMS(ba5j&Vj+N + +6f#aTQ1JNeElPhNVPLj`GVbDV%DYQDdZbmeS[j5Xpee4GLelLG+PS4`JbeUXka[& + +k0V$H4$f6H2FMHFHjNP0bI"Sd(Fh4'2DERk5`R-%10TmaEFjrI`$I68b$mrG)kq6 + +aHBBP*&LlQC0%8Xl9HQQfr9b!L@&XcMHPT*eJ*QI3,1Ibj`$iNqZ&q@YbPJ1Ha&! + +Tc3P+,rc(E-IjIaGE%9QEH@4l"'92bccba&FiN!#)&l6[jHikPAbI*GrYmVe9[[I + +)phhbr86Z2U8bGeIk!)'b%TGV)mAiNDCMGeGHc9GI%IUT&GqZ"BjUSA+ed+mA[-2 + +LXC)(FAZaC"ZB'D&IrCc3Ep!"HarI&r!YF8GmAD,SLj2'YmVA4CaPLEK2k0IH*6a + +V*Vk$fS9GI4I"H5aL!-[(@%*ka9$HA3N5qMA()VUDA4&9YPT)mi[cZX*6&cM@eJP + +93VpZN!!h"R3P6RiqmI$[+mN)k3@15PH6#pcRH,qPD`T@&9NVUY3'[UeNf`)(%Um + +4l0h!LdSHK&T$P4pi$qrR04'Md+mkS'(0E3aI&)EejF*+mAAAd"56T5l"Ckd*lZ6 + +dYG-("ec$9*M3CUehlN4&9Aer+0`PT+AR#H3GeRp3FMK[%pq9er8Y223JLKM!HEY + +N,mdU@jbA#DY@la65UhIkhK'(PTE4BPEM30kDR@@'[UIiiUc6TNIh["CTp`k2hPr + +5`jXLjbc1QSI$eZbmE28#KdHUPIB[)RkQV95-AKqV@,pZ+bUiLHmHp@@M''(eB8f + +f*6X2R,FYF5Vrc4ePeE6)rfDaf,5cCM&h@d69*`VTa,5qikYhmZK0Ble`+6c9aU- + +'$C(cf9ZKQl&q68LMIi$490Bh%PU%6PbL0f'aB1Hl9(X5aT1l$Kj@l3YE82GhXer + +JkbdqLcQ3!1Fk6iB8YmemmZL+iq,&A6dRGi493YT#@5[6iERXA%YphBr&!El1[CF + ++&dD44l1b0lLIpNA*b0Ie[@mhS`,[c9hpkT&bXm8F@aUa0,JLKIL@V(3KLJm!)8* + +&l+8LDUmD1G8`KVdmJ3fHfLH1XVUTHZhcb&J6TE``hq4Z-c@i`ef*B0pah)HB(K3 + +H'HbMU6,f$BBChH*)C%0(+c3dM1IjL9Re`SV`bmEQ#NIi'&Lk[$Dk84behl,DCHN + +H16RiF'r0K2I@`Gr,ZCIaFJ8(9XVm+EKbPreGN!$mr6@mUF84qbhVQ,I8i-1$d1L + +YqD*,(#erAVJEVY!Kh&Y92c(6UfI+c4%lZQ4ZC'U$+c`cjjFl(c$,5(pJUS`F$5# + +EZE0`h)YZC!jHBaAMZcmFjCGm1&U$M9+Ne&j+T4(,h&)bVh&lrSC-Tmk6jY8epT% + ++KrZQ`[0dKhfNlm)+9rKGp,K6bKpRq*MNS4mHqT0LLL3I0lp35RH%Cbk#'pph)mE + +6[h0S,fP#'NXTD5D86d2hbhap`Y5EHAZ(lFME$j!!1d1fSr"6Rb5lf@C@BB2jcJl + +d"Pmq29"SQ8HDhKll%9B0qe'T%Lq*l`B@mDEXREcc)d9M9,K%USLj(+VSJHQqK)Q + +BUR$*mLCd,r",+)phKPA01S'YCFRQb(lRkmXX"TYMlpHHARDS*k*$hLm)m'`$`C@ + +&''S*&!*9bDJjS-&YYQGB2'VT%G,Cl`MTLd2Sm'j5'3C),I`f)I@3!2%1,)HU+UJ + +[bkq[4qlc"L&GfMhFDr(rrZQrf[,p)kG15hMhd4&b@XV0CQ"E"aq41''CBqMY(fk + +6'%db`c6B2p`N-G`b3k2E`LC4PM$L%f0jKiiA$`FdZ,h'8JHGYGjZ,MFIA,hUZ$K + +Fiik-#KIi%CQcHi)c,(2FXEaGVJlG5DIV!UPX*XE&5&T'QM)AD5aPC#KEMpRZ(3F + +@d#@FcrhLGd[T9XjApG)IRkldZGhZJ5-RYrVI*)HP'-lr3A8KTMck#[J2AZG[`VV + +Jha3@r)a[((G3NfNVUYR5CUc-9'i"NmFYABR*P@C*M$5iH4*6"eEDLVfl+"l+"(8 + +@M14#qZ$f$FE-%Cr66QkRcbQN$fhIF,09`KM,jee+2Zp$4fakRpHZ&p+X)mlfR0d + +"PD(-NB(YG[A4!D[DjheP`1FGh"ibp'lGS''H'jf"FrF4Q`L4&ES+2A+LQ%dj*8l + +JqAe2P46cqDAU"Zq2[3hH*IV!V%Q9RJD[$Y[IcD0hlLbM[MffBNarf[!E,'IqV1S + +aElL)9fHGF2%%2`0UDi(dPMEbbl2c%Kck4I2iE0i!RV[80kDaL&r1U`2Q5CH@"Lr + +[j0%0QdI,$*Mbr0mIb&Vl[VlL6mAA(hfaa#pj@9j6KDPc$R)3I@Chp&h`$&mbSC- + +1!RXIf22!RJ6fYm!H!,BEf0m"Hh*LCMEaT63VNSGE8@5Q-%`Tk#5JFa%k+H!Y`!- + +bRJ6HK'V%dHZYf,SBN!$R'c'C1LBRd`93$,0Ui1jQlR&I`LU#Zje9!2GEQ52F,Ia + +k)@hM(PmfejF`2MlEaQ@pYK(Kfraah#la*h*F5bXCXX8fMUr1HS@dXLKKFl&i-D, + +KRHjGikbVar'Y9la$l2RB6pmR,LdS'+0CVLaC,H`"dT@r%Z!F2cScr3P3LVMhU0$ + +RDQ6lXmIBIJ6h2FZaT-(pd#Tr(GX$[`!BEfIS4+1rNEepHBe0*1LCXfaR!QFkYKh + +"[C!!E89`RpfiTTEKYhU%C9l5FSYb1eVZ[NShdqFHU(5[B[`[Xmd%lNp8ZZr%``V + +Z`-Sk2q2e,eY9c6DeamCH2MPq""hf),AJ0Z`'mAk4BHU,`2"fN@(D$$6B3eKJHLe + +ijh+BEJhfCmrNX"X@BR0iMP35pJI3b"!RLM2TKUm#`jj4mR%B@%X1Qrhh`&k8X3q + +"I82'4(M5h,f&[F[64H#l[1e2f"XKA3FdhPMh,0f#,XX(PR*-SARJ23cXC6*+rTj + +($GBeQHQ,U+Ad,JkXA`G[(hJpP*%d'S#PC1a"B'rNDPDX"RC'a[6!hT)eeX&I3XE + +f-%rDMYpUEQfrmLafmJQYmYTfr+%XjmL[Mpm65YCl'2rr!!d!#'GMG'9cG#kjZ@0 + +38%-ZZ3#3%%0D!*!3(m-!!%+&!*!%rj!%8&*25NY"5%`"!+QdiJLV-$9B!!"5l3# + +3"K+K!*!%$I3!N!Me"!i!pCQCc1abX2*Ef-,&mj8EA@KjV4fRQfkf--,fZP@[Eld + +Z$dq2VmN'A5Bp-hbAY9lHAJFXfQdl+AG,Z2)ME*&GEJRrA-libQIDl@-,fic`*fc + +6K5HKhAEKE`YIq-)mEQiRK(pXXmb@iapGq-+kKCfFELT3q1c,IZ&ZXPf1@pl#b%) + +ffjdZC,)F@FK#&m,)B+r,!D4[CPq-FBbaqZ@-eH&@A,@%-I9,M(@V+THFE3i'I@, + +PFV%p`R[E)f,)lA5*'SmV)SBMaKm`"H(DkkSAQQdeb1%*lP8%I"Kcj(3rX&H6m0M + +IZTkaqjrj`UCT$PZ9X*!!V`m&fSamV5GNj#ReR!CAb"Z-H0XpDBqF`ePa(%eGaiT + +)S-2EcP+HcTr1B+bXmm9Kh'q$6Mf`X[$"KF4R$RhYV2*CXk3m49H%V`fdL)`T"cl + +J+-2j13Fpcq@-E8&E8'&IE%H%!Ne3,pZF#1HDf2Hf""Q,&l1('*Yr8%EphJ1GXSF + +r%JrNr)3rGBV*(aq@mf,a)FC8Kq$ER2+`6KCr)B9h0"r'+0,%0Xm[rQdqSqFB2cQ + +eBU69f4*S4krcbhc8LClZG$iIR'*cIAh0I"abUXM3iXkAEq$(ilQ,49r!j3f+,H) + +maNhp56c112ejNK@"P6JkPXIB&fjK8aKcR!drZX6iG+jqq&li[TdQiqM4U(!CR@& + +rGU+(,&FBA8QAdZJ+kKT@q*eSAPdm1Mm9!Sj'C"RE!a%aQhqm(IAaK-)B'-FE!ha + +jS(fj'%,(Uc#'FK,*f-@9@FC3113DEaI$J@M)*3)Pk"9$i'!+Qm`pccf[0,(*#J2 + +h%ZcNS8*JE#k(6ij38,[0q$[cVaRB"FIjhRDA,pSLmUCDTmXQ1P[%8(M@V%X))mK + +*81HhL'j[ZmK(3P'46jb,ab@$h%jI@)iU6J@&a*8bd!J5%NZ'TC%NDKY",5%K9lA + +%%1kQ%f8Z9IE(4kQ5X*9Mq!UPK%dirih2+53-k[E(m!QELQ!-Rl#ccq$6B)6Z-I` + +FQ(52iC0Hd6f'2a&QlKPm`YDG`5GX%V)aI-*'%r+rq)3prJ`qB9260)C2f"21i"- + +feI!B2QRI@@I`#A[5'Ic*-1NH`dIV+GeMrFY8Q(52j8mG(mdXar#TGUKe(X1R`pq + +T1G'EYSlfTT4IFZ446jL-RfpLA2G!eYX*@kf3!1dTXPdLfkfbh5AE'fAlbB5G8j' + +`4rJkCZFXKT(SUhpj-0jKc0+KVIl1dd)2DmAG-GY8*93X&AUb"HYJr,'#0E!H,EJ + +1NCe#Mr)KS8HMKZmGh)rJ,V"iE"haZ#h!9,BPYJl''HE&0`Sp@9F+$qSClfFqB9h + +h3F6FlY%JbNC43[653pSVJdcS86hQ89H[mbKL98+8Rk[YF1I00PeH*e3+2HTqAYH + +N,LMMCc%HqGX+1SASE&1&f@&'l%0mMD%M4m1VBND`e)EiiS,VCTXD(2B'40m'rl5 + +#08#c9pE!hmAAm#U26ZK4E&E48%VR2LJ-CTF+Lq-[Q!rPj"[UJRc-'14f6EKm3Rq + +[HC!!63aQaBb,eS*44IHY`T9#9"TN-1YJpRX&fl4AmahDMZpMp-1B4i1Br38Ef*5 + +LZGT1Yf,T@L'kG+hYpILK5iVBA1+i5A[CfL*0plhmp&KCF6DUCir(CadF[VkJLmr + +hl$189GrN0XCQaUTQQmSPVV*HpY33GT)apN++X4le+M"i0Epbf"EcSZR0GUYL,E' + +CL0P[#,$5,pp39-AQe,`b2HjB@cfAZmLMk)i,dH$ilTe,er+S69fpF0LG9mb$!l[ + +R31a#i(BDla#LU"ri@"l9MH5GKNUFPjh[CUb%le$F&p6Y@VGPQf+Mf`$HhiaG`0F + +EE!CpNpCmJ'NLh(AkA6XZh4NrZ+jVe`eZK4!eX*L4F(JZ0X03ArHcH#pICpR!*Pl + +XK4j0L8ffh'rc-KeIere1L4i-[$eMkE2E5r8'IIXP(S2Gl*Q)Zf#a'@X,Qq&K$)b + +8&-E"[@,S'A[+pp5)VrqCMI&KiNfa[Q3Qde9lQGE01baYqAD,Zb2SkYi*qa$K!H( + +QrQk@*rZq5ckG*6lNDIDh!N0&FHA[kK@2A1Tq5ZHFEh)rKLLeYSe0M3qAR,I8E&J + +jY+[rT[A9)lQhp[p4)R[CAjVd`eG)q5Ap59[1Ed$+lfq3!*Xb2P4bhK@8@k6rTRj + +JV+rq[$NqA2U`m"9NK3VKAUem9mqHIDj8lbP"PFc`j0R0lNQ*I,N$6AVCdp18*hY + +f0%'EZEh)H$fUN6,B3ica+pmIjZHp2ebp!DT9@&,)#Mf''B9-IjQPr#f@rm`"TRV + +fXT+Kq5E,f4-2X#q@$(82A'Tf[iND,j2dTmcpQ*4$$h,S#F8M6-VMR%F+f4IGNqB + +J'pZ22,VGhpLkJDP%PD'3!+P'N!"h!rF@[MkB[ljcr`h&frIIb#bGV(J(mUN2X4* + +pX9j4GNhmp4Y3'hcTK+D*KTP-YEkVC$Za8E*$BZ+*q*Y0FrMmf#+ql$LLcLXFCJU + +2[K5SU)%*YQ!q)e6KX1%9i!l`mjL@,h-VR'U"@M4@E)Vpm1i&"NfaDF-GpbrBfZ9 + +43qpR0r'kZ8c&&BRN0640K&FKHr90+PMRPJr'GaLkK'MXKd,di#&8q%UQd23bTI" + +9"Y@$aT[+kbSUjl2Z'0pB$phR08+dF1AJHN20YhDrGZhcfjrC,IPAlKKLCBC5[4k + +q9Idh5c&Z18Dc[QH`6BT`b"(jr6f$$LR#)NHSe0H#a(a5Q2KG+Ee$aFHh0DPJl5( + +93@8ePZK,p9Z@,YNC(kbfH)D&!Aj)MVPY*'C3MV'dDpHCrHTGCHB"TLM1TeLdU%9 + +-9@4Q+N-4da3eSVGlhF4QX!,1CRRd4iAX3Xj@qF4Il+k`@5b@hZfl9Y@m`Nb'kFM + +m(e%[4TI(rJ6aDdl'AmecRb,-rM4HPmkJZV0Y@[@eEEU+cSTV%FR$LPDJFf96T)J + +SBV95T"T4851Qcr(ieNkAfS!@ABKZ@GfXkpaZ+bYKPM*EQ4$GZVVj(+2NSbLEp4* + +QXhjcHh'fc9U5,85T)[CflEd"+)FkYrHZ,P(Zk$8UEGDRHfh@rY@LC[fUCKAPh&$ + +@Y1rVM$T#D)9kIMCdBMTe139Pm1GfheX`RFmY90UY2l2DVI1bQkD-SR6CVHVV',Y + +QH0(D)YCpAr&dG(pClTG)CrkkmRDVHaU[M*8KLl[iXi"f16cV#a[iKE'C33leSVV + +cA&k$1%ZK,B8aKer)+j[dSeNDl&DqM%FeA$0FT%'A9r0mEmcBIIHPIa9riGZ2&Y4 + +)Z5bXVN6AH6jd%(9@BZSH+"mmR)p+fJ,I1r!p$0mpm2dGI$I#GaYmI`rI25-pFcj + +Ib+CiY,#QH5B*Jb`#R#"`$J)R!Rm,r%fb2`5r!f`%81ZYQ*CVS1I,dCQD4M[6f8" + +d%aZ`,C3pl(R%#1`5BJ$fKC34E!2I+%5,Z6XAc,!&GAHH@mc&V-9$`JriRE!1mdm + +QBJfY6"1EAXca96'V%%d15UJ[MKrdU2JbblTde+I(r2fRV)GU*0F[GKFZ'6FZ&@C + +!@&e$S`1V*BfZ3,[Ekc'f'QM#1TGaI6mfFAd[dRd&lTYa2mhe[DcQqPkGarAYVFD + +pRq[EGj!!kh[Gb2@pdFVerHebVZqYjlLqJ6bZladIehI`(Ul[(a4Fhf(J[@rMqRk + +qJHZ,jh2ph!,FAqIkPGrNqY@YA,rQDG`$A2piD5R$)dE#I+49a0+%1a6`miQp3Qa + +bq2hBFJaMcC%A-H[Lh9kI1084#2JDa"!f3ALEk![b$C%30K$$+Rp)$+Z#lAk4M'@ + +U"BZ%FY95Keh3%Y-m5!m&aNNZUbm3$MY$+e3GhSKrHRQY-ib9%UaRb2XM&r&Bb[Q + +$#1m2Y(MG+riPr[FUR"'4$dHFrL$[$S4iX30Jl8iIhq)0r5khhm926M)p@LJ6T9) + +i'P,4l,[)jI1kP[&L+-6l`aiMMHaaP!k@(kR(!$5jIF64)2HV9c"fkm2Bb8M[NA, + +5*ahe$KKB9T9'TSPBKI4**`H4UR2Kk*+M&9J[`FHC*Q&NUD#pVUA83F[45Jadk'0 + +F3Yf1$dpTM65,Hfl&AGM3!#1U'a&eQabGKF82I&eA%c-D$%HjjT%"U4TMFAb*[&A + +h)@)HETXFRBf&$h`V0NVHj1U3!,`K#cY(qL511H*j`3MI14L%iN0H')LU%pY@kEb + +e@+I!ap@!&jDr$K6[395bNR+a,%&ISM6!LST@Uj*V5MUX3Y#A)"$4+kM@NKY`il$ + +S30pF$R`T#q@S*(BHeKMSieHp#Flf)`,0AQTaDcb@&2)PHQQ)5fb5Xdb1cXF+!Vj + +N8DB2,Ic5f4Kjid'T!M!XRlE0,$48%8&NcjVeLhiPLG[pfVbedR#BF'qX0CFl+(- + +SP#2N$)DCki1*FLTMEYAMF%qMfLlECUkT+5IZR$kIUlACYmcS)YhC12(&iZ3YB9' + +@5Q5*+ZHdkID)X$BCAmp+hXKTKT6AHm#U3r4C*hSQB(BrU*ZE[*&EJ[hH"NF&f1H + +b`j%@Ei"`&+-i5TRYhSDUbbZ*lE"hTGJB!9#%@0JA5pj3Yh-5l&V,'fQFRq0a03C + +$hZ956TYb(mp1hP#k+8NN)bQBbZ-#L*FT4c0ATc*h9&5!)3dB`XSCTF08SdMC5D3 + +Pj6BcCAk9Up8CNNK#jN9IDNVH8!QCSr)k39+0G(N`aFD&eSVN$99-XdNF%CZY,D( + +`"a@L69D5SkS@&F+T)ekr#"MM-CcF0*pfUMM`5Hd-*A450pjlk`mPT8VU"Y9h0R3 + +Mi#,4b)#J'D-9V[Mh#PIqZX**-8jAH0BrUp"aT*4UR0)#8Sh6@T!!8Se6@T!!maX + +Yd(kN"FGd1[HIG2TA[3DH,8Mf'TBDXp4V02ZFVQ8q2,U3!#'KemM%T"XRp@#KVcU + +Y"q@f5Y+$A#aMZCD&Srj`4S3qiL3hckljPY445pa8@+b09#FYcCj'[bpc@BGcr'Q + +!%69iq@)m[C*8URU(RG4!'ib%'PfYVS`*8j,-6"h[aReIXbG[D8k5c,e@cYh[$#h + +lT)pilFFr65[(JLU"+N',p`QF2Y40KM[Pq2-plHN1e&CT4R@a((P61@0C"rU4'Q` + +blVmMh8FNDTaTr9MRD@`4JjR-qSM6-pGM1,T84T8160L3!*%BDI-(2jh'hIh8YR5 + +r8BZ42Y@"2cR5GhfQ,m$+0,B(FZ(*qFCchdR[JG5Dl3[K98[0EFBhc6Jf!k'Hj$p + +R)(rUIIG)ebZT#lVHd,,'8%3DJQ5UfdlEP"@LKiU5A8P9!ff@U2hH-(@biF`FQ[( + +KV+6++NJeiI9JS(a#A@K@FPTGe,p@Pj4QR&)AdSc6kT,5M&2U3T15dqU5QT4mULl + +T5FPrl#eaeipXJ`L95k4YN!"fmDV'M(FlXp`hrMJpBDZc9%XlCB(Q0M6#dJJhdpT + +%2bZdFd30'KTT[d-6#2rA22prCQFCZHEjar[pNj2C69PYp)K@DM)V+8'fT!3C%RU + +0$!Sc%%F&0K8NII&jQb@NScQPp1@%DKc0DD4,rDbV-ccd@PV(lCAPY$H4%a*G2UI + +ARl'MdM)(c3+5MpDF8)f1Rr4*kNc)faB*9I4DMcVDlZfJPej1UXfAEck8RMde1"C + +Ci0@')p(QjN#S(A*Mr%a[J*8"E)T3G!%pL5YhHBl+"RVj4bhpa)5,Y@G#d)*M[FH + +rp@3IGap(N9*kF+TlbrUSQrlA5IIaD[aidXeYj&CVNMH83&CM+!&9RaC+%&Q"[`% + +!PM5C'9(,)ph(*fUTr9!YMqT9DV2iP&iGfErj4+r'r8D[mMkHFibb02iMPNjf1PA + +[d("$VLh(CI8d(p1LX&VN*cJbP(8k[pfF2kE#ZPqTX(51-%LC%ZXU[a22)[*i8[E + +rZJ[cIcUGL4G#pHMBk,e2kCF0VX,2PP#E5Iik[#T1$qmHrqXJc[6'Fa2`XLUETTM + +$*YV-$D3cYp12%m#qEb(qhJ$feL8eGE5PqJMF0!YqXU&'QZAY39+9b(8[r8`"-MX + +Ah$6![T!!ITF!pTb'bfV*EbNA&PMaKL[H#UA+i@kTX"!qGeH&C3R&EkCI&X"$k6d + +9PN9@f#m[VUY"R%+aB%N90%@4PhahPUZj([c3IkY-$A%eUr''+[Q8"m(LQS3[kcE + +1G+!PiF[1j8b6mBiYqG4I![EZK'rFji"Ab"55leDmdYV+9*,[$[MHa&2kj,XIH(K + +90KkIa-Ep'I$!Tj5(&h&2b4cN`,G2pSf$$kqZ5Vi*m(hh+pHLCV(B#pqMEAp*2`L + +K$S-ce482X[1!F4&mDd`jE#EL`-(e-DD6q,X(FCd12IXm1+#IdU#-2SFi1q)HB*d + +54KI`ANVie'C`8jVJFZTNa%85A%ip'ebqP1"bkZr$jj-acJ0'8-Di!,i@'@-Q-2E + +*q68KTiMXZ`ja[9RqCFj@hp%rG"RpQjINMlqNrpQ&-qA@"ki53rAP&2rr!!!0$3p + +YGbpRBh4PFh3Z0MK,,VN!N""453#3%#pd!!"+8`#3"2q3"%e08&*0680$!3#V,jH + +ZUc!jB!!!"M%!!"R%!!!"V3!!"E(*MaZS!*!'[VXM4!iL+Pj0j%)PIdhl9fbRBC! + +!DR1(JAFp3hUJ2KNcZ@(k&LeHlIYc*cMM1X2GRCf"!*`N(81C&iAQNTm4&Ifii1" + +EpGII4h6#PiP+'R-jb[e$&IeM12rA3hh-XBk+D2XK9#@U!P9e!@eRU22XRT!!%ar + +%6jaP3[FjFKhiIjQ@hidE$&25cAm$`-IrIXai*1U*jZd88q%pXX1%F$M`RNJbAQS + +ih%%N0J*@A""6p[pE#%1,cL9X%K8j[Z%i38$F)*'R%8!QpTQQT&06TCMf4amme9+ + +jii[1iC(HE43E%aa#QlrCjZ4[GSL(8*!!e8D-E"#r6LR@&GN3aF6F'028K*cdTGk + +aT$fkUhhK6F,P(Tj11!CFTLJ+QQSXDINp,M$RL-+Cm9q6j"VK+Hr'rhrjXB16b1@ + +iec&AC&Z,)bAP)A[QZNkT`brFF9bj0@L(b*(4H3)$i*YCbh9`YK90aj%$0a!Gm&! + +,de[B3!XlC'%$"-Eme,D0'(Z229-8DlB`9Q$FC!Y6@9L'KA%@PQm[")V0YM#PKBP + +$[mI#m!L#i#MfjAH50i4eE512Q3bj@@90I4m!N!--!'XcXfpJlh2Ij$4lRaZHF-P + +a`Tr-D)4&@%FjIAiV9hi5rZ3i@3NqRhV5`hI'm8m[3MNjENHi%AjN`!NMR"`rbB$ + +bTrc)FA,m$%r*F51Fm*03FTa`FTa`-Q#%%hlN'4R`Pa`RA(+FF+mMamRa)mq2m$2 + +#bB!#GjN8B'@Y6-+0iUpN*rl)-F)*2m)*8[#%!j-9H"9SN!!()1QkKK#+`Hm@K$S + +HJ&m,rN[#E`hmIJLEJ,q0bk)PQTCS@&q4J@q@4d"9U,FU)md-(0Yrf-'kLSC3Ech + +QTZ6PDfM!,6kXTJh48"8c3%-B$Af2ZR8CG9Ip2$-35k-p#&9[4Zd)$4`EE%%G46! + +,R0"9-23T99CN34j4,-#2%@HJ4P(6T'aDQa#N[iMDX5G2a3J5j8hqU`G8AI)J-HU + +[2pc+8DXTel3Q5K1DDDe`rC'MeMLS#5QV5"2QC-jFKV@(Y,XiDUf$'TI6Q941+fY + +NIrEXmabeMLSdTZC&6Ae8m48krm8h(,@HFXdUSU`BRMk!q[lRHBlD3,RQ4#QENT@ + +#"cXRI2X+4ie6jif)dMfM+mkEUadrc9%E(G5'h+TKlGFqRHHS#3He,LFDrPe`h($ + +QCBlDa(3e*P+'jG["RP9riDM0,PI9V"`8d09SikJYP'YH1C5kHVfHlZ'SDkKIpI4 + +i+LIkaJ28)bpbe,88e9!N694cCG6ZNqFjkMUUN!"T6DE6ZT(h&AViKGmikRVU"NX + +TAdR(H9q1FY4@bY@D,XL9SfF2rY6286HiPp,*+'9G,aJIFG50p#Uce14Gj3Y'd81 + +Ek"h5cFV&)blrQ+1f8B8b8UTJU&0#eN-9cVh+8GXGe*U-j!-kU)P6p4b9*UB'dj* + +PCDb-E#IIrF$K4qBkCkfIRK)eFi@ZrFEXr4ae-h@$T1I(e%`C&K,!AUi3T&L#1U` + +I'P&bCG3h(rRp#Fje+d8&50fBrKHeFp&j@4Q5M3GV$pea1eGSfk+(0$9pa80R1GF + +ZCkfce*a5FDbGI1mKMRSpifUSq482fFRj!BlD6Id+#UPkaDr(MfcMU0YGVSSeRLY + +8Z0V[F05H43q4)19lk0aM"lL(GMKViS"LkT1'T(MH+rPeTkZ3!*U"!([&H8FjkLl + ++0@RS306mKfX[64ZJ+`31D"5@fGUaCaUiVRd8Y@!C+5NVP42Ef6h&a0E[S,D5e*Z + +k$e*4k[,4R"1qUq@S0cKV-k$Hk86c@fiEqT2V*rYSlLHcfePEppip1YM9Hl2Del9 + +2!&`"@TQ,U#F1&[Z''jdelZ4b1(ZHmdimH"0(45eR)(&!*q9f)f6q6PCX0VTUBad + +IAd$pf!@`[ik1Br'KUlR)+fakrN"cHF(H36)2h%jb&H(+NrX0&jMF9VMIj$*$&L) + +T"p)0cLf`Yq1%"AXR6JQ`Yq'FKMf0GB,GdbRXPYLiZ+lq4#IBL8k`%jeJ*cV"6R5 + +#RHJ%1p%*GU)6l%3Rf)P1h%qc#+[@Y15RS-eL8qhT&"fJcd&k4dVkK,dC'pb'AVi + +MRZjKXmB'HccD3(IrcJ8G(KYmfk)&p1R"5Hkrqa'fKQc`$Bdfm0&Ek'dF5*Cm&25 + +6E"T+qQc(16M5i"iI4FpKHCCb3p#-XSR6I3[1YF$(e@dVrAm(hAhGA,f#1a4fVQ` + +D)a0bM1IcX19PNiJXd-QrQrjp$rTP0Nh4$ljDEE6C0*GdfSPEQNJ$[AaI"9dkQjE + +)"&rjZ5PSlpQXL6c)65I42'&jkHi((6HE659pGY(F%GhJrk#CBp-AQC!!QcfG`RF + +BE0C'2GbTm18(Qh@4"hI+cbI"'a-fkb-2I05,Qq*VI86`ZS90Dq6"IEUNPpZrZ6d + +IkmP@hp@`f9$5UmK,"LjZ2dGjKIdd'pTRSrf,Re6[[[HdcbYXX0R3aK[KcVI)#mr + +A-dm"R8jJFcLjAc2T0r!1Xr%Ph(NRKdhm"Y1PM9qd9#9(PFc#![X)[SNKr!e@jAm + +!N!-0$3pYGbpRBh4PFh3Z8&"$,VN!N"!4c!#3%%+&!*!)rj!%68e38Ne33d-"!+X + +[PkkV-$P&!!!'-3!!'Z!!!!'T!!!&bE5F%03!N!B"fL0%$L)UANhN3L9r6IYAE+G + +KN!"UFiH"Gce$HU!q'61jBIV#iB$[cjhJM1X-GhH'!`%ib6Q'-Lm+c58r)bVkFF( + +"YqU[[irS4$#9MENFjIkKL[iaR2rVS6lQ@%G&Y2d3UK*9JDUkJ,Bce(Pf6fJm&6R + +b2Z8HRJiXa'A+ir""h#2TreqK*11PKX-G4'@dI[MrP@fl(cXiL9b1Haec4BbeKmP + +aeJj"iNA$iL1d#Y1J+HR89#QQrG%86l98l[LLFhLNlhad)NaL2JK&0pZFr-d1m4! + ++XYS)fcSm[diTeKAC%-A8h"M6e)5Fp+AHXD3p1ZNm1FY%rabj$[`E!$0bi`E$P26 + +rG@!p"$aQr-JXH*CjLX,-Um9UPGj1-5VH)fY@`*(4VHaDSf,&r6CPrlq&--R1K6X + +*#r!9a`Q#"HZ@0$hdcLR&Z$Fm-LN%a%6I)NG'j`NF&EkCY9`(CaX9iFL4(fpK!IC + +B8#c-*P,XP1dG-@D4KE%@0XR#9"C'PcdhhF,ZXE"3#eYVB3-&a[CDHNU"FB-@YXI + +#PPJD!bcX5f0T(aH0)DaV'hR-C-M0+Q[Uq``!Da0l'f3fmMSr"jhCCQZ%N3NRNdf + +14LJRP"rPR[a@3Sqr%8D1NjAJmk5Hp2#G-Ic6Le"1MJm)Pachb(2###I(6c*J%8k + +j%8k1RiHRj,J46[K*+$P11$P11"P3`JNrmS`-q)!-Z'6!D6eKj2L4C`f-F$+J`(8 + +Q"D$m9QE4e,T1r"&qK,q%%k6J#3FQ+c!qS%%HJ+LU#N)S",rE%'S`i2Fjq"D$ha, + +iI4qf+2P[K53BJQi)Q['&0I#IjBQL)Y4CP"42pjcUHm,'ZSf'8'HBF--Ck@qdLS0 + +b3K-d'HXH'L*+S#ZS9C93Dp(hThY##E32SH*'Y!@KRP2p0@MV!TJ"6QM*DZUi,'% + +T+JeJ!r"$PM03TD!SBLUKM%E&Qd60d0-c)3Z*mVDqK3&9&I13!!6eTfr[iUM&P'Y + +#%F4446G@Z(l(88YXe)LB`Z+S2TE@Pf(0!mTp(,A84Uf3!",*H&STD'4qmZcc(,@ + +-+M3XC`4&IJbl#Phql%Z1'UCF8eL3!,#@e`G3hrdd`e(,+GHd)+EL%XlQBDHLAlh + +-85ZSm`B%mB'K&HG0PBjFj+L90QTjHXf`jUXI6h28L)eDPKBdpblBE[Mm*BjDaA6 + +94Z1DiGV"R*4rj+M9$PGCAcdSS+ZfQD2@8+iCI$4qqhSpdmj4DkPIeF4)2#fiaJ2 + +8Bbpbe(889F1L)XMT!QVVKFXFYBiUT"YMLB5UC9b&(RhKCikkRVV"`)8VDEQKZf- + +V4kfRA)f*V,4kp-cqUedFGB0c+Hf-8Y$eQ[B"4pe)Vc*,6IQVI%eEm0!QHSG8IIA + +L@5lrN!#MEUCQhhrr8(p,Ec3@kie#4,V"pIbK)a`9-T!!GBlk-`E@KJ84,f%LG'i + +f[T!!!'KLUKNie$XiPM(N0&lQH[KU'dGYS"j+L['X*Sp(8hPGSl0R1'UMM9U5&&e + +!'c8b%qDSN!"L,3rTKL6Ki3+b'A[l2CZI0G[Y06`a,LMk#PhcpFQ(1'S6pDZSCSE + +PC!&fUR[r&Uj3-eASS(Td!+F,U1H1r2)8jpT#83&5e5EZS1kBGej+JZb9Kc82h(- + +h9kKehN1+R,MPS8ZFDjZpPPPABF@aCZbG`abeRA(9j-b+KmcBG!p(lD"q"B9NGG@ + +[CimeFp5G$PGXM+5cUec0YcMUVRN2@9(2pG$Xii2F3jhf'KR%ZMUQL6M[[CaIGcX + ++b8Q)f,HFGj+MGP'Z-8d&S[SrA2I32!5k3L5#cQ1CV4NkAXjer4pehS"JT*BMJmh + +eq+jHMVUAFXdD@Pa-LB8NHQRI3K)PI3p-0D6jHqhb!-,0lkJIrAq#kpTYIkZh1S$ + +iJj!!%H0,"hSUqR8TjiTU6d$LH!3qd"l'QVp5(*Z0MQj%N5IR8$IK#2YVk#b4%AU + +KAhRjVG*[D*cA0T*HB1mJp`hf9R+*B@mR9a,f0R*MBGp1mJVX655"`0j)XK,X1mL + +pKlf"*+irG2*l,$B1E[#"6T2S$,#X@[56ejba+FlV&"bJcm2dMZ6dm6Xk0U4jAES + +MHGhp&Sp0DH#"lZkGmrT#0Q@"!rVX)TRXhr[K0j4X`S%(2RS$[3RXDKCpj(@KE-T + +cqZ`NL6E3i"kI4160319LE["D@$B9G'mQ#4Ai1,Ued1qGG(GeFr6blT!!hqqbU3a + +-b$&jRrZ0-TY)B)&1lYedll[ACE1T#Rl`e9TlcUBkTp0ZdVF%-H4Z[lGR8a1Bi#X + +h0hN["GM8"KlNTJYSfQ*jrHjlI6UE66PpZQMZ#'l`[pH@XGNEQ*!!Qr-kq@mqf+` + +,HVK6rLX60R@""hI+c5IHHaBfk`-2I,5(G,jrpK(H5aSfpB%(pqQkANlrj[4mV#G + +EHm2$CN01V`9H%R"aqMR+bhpj`iDqe%&p8bAIR!qTj%[4$kpFY(MK'lcmYcPXk&Z + +H1lcmlTi0lIT[mPVbJIFUL!elGjRM4BM8c8"+#$@"@kr%qK5GrJGH8d5JeDSp%6Z + +S`aY94TZmpLQ+$H(Nh"cl%r`RK-KrL#Vr!3#3!aq$!!!"!*!$!43!N!-8!*!$-Tr + +lRLe!rr#`!,K[$#eZd!6rm2rdd"lm`FAKdkSV8FY+$deKBe"bEfTPBh4c,R0TG!) + +!N!06594%8dP8)3#3"P0*9%46593K!*!BUc!jI3!!8M8!!!&'"1"2l'mDG@6JrHc + +K@5U#NI*HN@GK!Z"2kQ`FG&2UN!"S!!,L@5[48(adA`CdC!EJ6qj[8hJS!!EJEHl + +LEe5!)D$!FJC1ANl!*IrX51FI-#D`jL63G!*&0K!+1Li!&Ri!)VX-S"lbUKQJ(Z` + +3!+SDI!$!#3ZT8,aIE!!!Q$!'8!6"aG!!N!-3!#X!"3%B!J#3"`-!N!-"!*!$!43 + +!N!-8!*!$-J$j(l!@#J#3!a`!-J!!8f9dC`#3!`S!!2rr!*!&q@G%'@B: + diff --git a/gc/Mac_files/MacOS_Test_config.h b/gc/Mac_files/MacOS_Test_config.h new file mode 100644 index 0000000..c95f4bb --- /dev/null +++ b/gc/Mac_files/MacOS_Test_config.h @@ -0,0 +1,91 @@ +/* + MacOS_Test_config.h + + Configuration flags for Macintosh development systems. + + Test version. + + <Revision History> + + 11/16/95 pcb Updated compilation flags to reflect latest 4.6 Makefile. + + by Patrick C. Beard. + */ +/* Boehm, November 17, 1995 12:05 pm PST */ + +#ifdef __MWERKS__ + +// for CodeWarrior Pro with Metrowerks Standard Library (MSL). +// #define MSL_USE_PRECOMPILED_HEADERS 0 +#include <ansi_prefix.mac.h> +#ifndef __STDC__ +#define __STDC__ 0 +#endif + +#endif + +// these are defined again in gc_priv.h. +#undef TRUE +#undef FALSE + +#define ALL_INTERIOR_POINTERS // follows interior pointers. +//#define SILENT // want collection messages. +//#define DONT_ADD_BYTE_AT_END // no padding. +//#define SMALL_CONFIG // whether to a smaller heap. +#define NO_SIGNALS // signals aren't real on the Macintosh. +#define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. + +// CFLAGS= -O -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DSILENT +// +//LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \ +// -DREDIRECT_MALLOC=GC_malloc_uncollectable \ +// -DDONT_ADD_BYTE_AT_END -DALL_INTERIOR_POINTERS +// Flags for building libgc.a -- the last two are required. +// +// Setjmp_test may yield overly optimistic results when compiled +// without optimization. +// -DSILENT disables statistics printing, and improves performance. +// -DCHECKSUMS reports on erroneously clear dirty bits, and unexpectedly +// altered stubborn objects, at substantial performance cost. +// Use only for incremental collector debugging. +// -DFIND_LEAK causes the collector to assume that all inaccessible +// objects should have been explicitly deallocated, and reports exceptions. +// Finalization and the test program are not usable in this mode. +// -DSOLARIS_THREADS enables support for Solaris (thr_) threads. +// (Clients should also define SOLARIS_THREADS and then include +// gc.h before performing thr_ or GC_ operations.) +// This is broken on nonSPARC machines. +// -DALL_INTERIOR_POINTERS allows all pointers to the interior +// of objects to be recognized. (See gc_priv.h for consequences.) +// -DSMALL_CONFIG tries to tune the collector for small heap sizes, +// usually causing it to use less space in such situations. +// Incremental collection no longer works in this case. +// -DLARGE_CONFIG tunes the collector for unusually large heaps. +// Necessary for heaps larger than about 500 MB on most machines. +// Recommended for heaps larger than about 64 MB. +// -DDONT_ADD_BYTE_AT_END is meaningful only with +// -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS +// causes all objects to be padded so that pointers just past the end of +// an object can be recognized. This can be expensive. (The padding +// is normally more than one byte due to alignment constraints.) +// -DDONT_ADD_BYTE_AT_END disables the padding. +// -DNO_SIGNALS does not disable signals during critical parts of +// the GC process. This is no less correct than many malloc +// implementations, and it sometimes has a significant performance +// impact. However, it is dangerous for many not-quite-ANSI C +// programs that call things like printf in asynchronous signal handlers. +// -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +// new syntax "operator new[]" for allocating and deleting arrays. +// See gc_cpp.h for details. No effect on the C part of the collector. +// This is defined implicitly in a few environments. +// -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be defined +// as aliases for X, GC_realloc, and GC_free, respectively. +// Calloc is redefined in terms of the new malloc. X should +// be either GC_malloc or GC_malloc_uncollectable. +// The former is occasionally useful for working around leaks in code +// you don't want to (or can't) look at. It may not work for +// existing code, but it often does. Neither works on all platforms, +// since some ports use malloc or calloc to obtain system memory. +// (Probably works for UNIX, and win32.) +// -DNO_DEBUG removes GC_dump and the debugging routines it calls. +// Reduces code size slightly at the expense of debuggability. diff --git a/gc/Mac_files/MacOS_config.h b/gc/Mac_files/MacOS_config.h new file mode 100644 index 0000000..93c3c97 --- /dev/null +++ b/gc/Mac_files/MacOS_config.h @@ -0,0 +1,89 @@ +/* + MacOS_config.h + + Configuration flags for Macintosh development systems. + + <Revision History> + + 11/16/95 pcb Updated compilation flags to reflect latest 4.6 Makefile. + + by Patrick C. Beard. + */ +/* Boehm, November 17, 1995 12:10 pm PST */ + +#ifdef __MWERKS__ + +// for CodeWarrior Pro with Metrowerks Standard Library (MSL). +// #define MSL_USE_PRECOMPILED_HEADERS 0 +#include <ansi_prefix.mac.h> +#ifndef __STDC__ +#define __STDC__ 0 +#endif + +#endif /* __MWERKS__ */ + +// these are defined again in gc_priv.h. +#undef TRUE +#undef FALSE + +#define ALL_INTERIOR_POINTERS // follows interior pointers. +#define SILENT // no collection messages. +//#define DONT_ADD_BYTE_AT_END // no padding. +//#define SMALL_CONFIG // whether to use a smaller heap. +#define NO_SIGNALS // signals aren't real on the Macintosh. +#define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. + +// CFLAGS= -O -DNO_SIGNALS -DSILENT -DALL_INTERIOR_POINTERS +// +//LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \ +// -DREDIRECT_MALLOC=GC_malloc_uncollectable \ +// -DDONT_ADD_BYTE_AT_END -DALL_INTERIOR_POINTERS +// Flags for building libgc.a -- the last two are required. +// +// Setjmp_test may yield overly optimistic results when compiled +// without optimization. +// -DSILENT disables statistics printing, and improves performance. +// -DCHECKSUMS reports on erroneously clear dirty bits, and unexpectedly +// altered stubborn objects, at substantial performance cost. +// Use only for incremental collector debugging. +// -DFIND_LEAK causes the collector to assume that all inaccessible +// objects should have been explicitly deallocated, and reports exceptions. +// Finalization and the test program are not usable in this mode. +// -DSOLARIS_THREADS enables support for Solaris (thr_) threads. +// (Clients should also define SOLARIS_THREADS and then include +// gc.h before performing thr_ or GC_ operations.) +// This is broken on nonSPARC machines. +// -DALL_INTERIOR_POINTERS allows all pointers to the interior +// of objects to be recognized. (See gc_priv.h for consequences.) +// -DSMALL_CONFIG tries to tune the collector for small heap sizes, +// usually causing it to use less space in such situations. +// Incremental collection no longer works in this case. +// -DLARGE_CONFIG tunes the collector for unusually large heaps. +// Necessary for heaps larger than about 500 MB on most machines. +// Recommended for heaps larger than about 64 MB. +// -DDONT_ADD_BYTE_AT_END is meaningful only with +// -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS +// causes all objects to be padded so that pointers just past the end of +// an object can be recognized. This can be expensive. (The padding +// is normally more than one byte due to alignment constraints.) +// -DDONT_ADD_BYTE_AT_END disables the padding. +// -DNO_SIGNALS does not disable signals during critical parts of +// the GC process. This is no less correct than many malloc +// implementations, and it sometimes has a significant performance +// impact. However, it is dangerous for many not-quite-ANSI C +// programs that call things like printf in asynchronous signal handlers. +// -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +// new syntax "operator new[]" for allocating and deleting arrays. +// See gc_cpp.h for details. No effect on the C part of the collector. +// This is defined implicitly in a few environments. +// -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be defined +// as aliases for X, GC_realloc, and GC_free, respectively. +// Calloc is redefined in terms of the new malloc. X should +// be either GC_malloc or GC_malloc_uncollectable. +// The former is occasionally useful for working around leaks in code +// you don't want to (or can't) look at. It may not work for +// existing code, but it often does. Neither works on all platforms, +// since some ports use malloc or calloc to obtain system memory. +// (Probably works for UNIX, and win32.) +// -DNO_DEBUG removes GC_dump and the debugging routines it calls. +// Reduces code size slightly at the expense of debuggability.
\ No newline at end of file diff --git a/gc/Mac_files/dataend.c b/gc/Mac_files/dataend.c new file mode 100644 index 0000000..a3e3fe8 --- /dev/null +++ b/gc/Mac_files/dataend.c @@ -0,0 +1,9 @@ +/* + dataend.c + + A hack to get the extent of global data for the Macintosh. + + by Patrick C. Beard. + */ + +long __dataend; diff --git a/gc/Mac_files/datastart.c b/gc/Mac_files/datastart.c new file mode 100644 index 0000000..a9e0dd5 --- /dev/null +++ b/gc/Mac_files/datastart.c @@ -0,0 +1,9 @@ +/* + datastart.c + + A hack to get the extent of global data for the Macintosh. + + by Patrick C. Beard. + */ + +long __datastart; diff --git a/gc/Makefile b/gc/Makefile new file mode 100644 index 0000000..cfbfc45 --- /dev/null +++ b/gc/Makefile @@ -0,0 +1,445 @@ +# Primary targets: +# gc.a - builds basic library +# libgc.a - builds library for use with g++ "-fgc-keyword" extension +# c++ - adds C++ interface to library +# cords - adds cords (heavyweight strings) to library +# test - prints porting information, then builds basic version of gc.a, +# and runs some tests of collector and cords. Does not add cords or +# c++ interface to gc.a +# cord/de - builds dumb editor based on cords. +ABI_FLAG= +CC=cc $(ABI_FLAG) +CXX=CC $(ABI_FLAG) +AS=as $(ABI_FLAG) +# The above doesn't work with gas, which doesn't run cpp. +# Define AS as `gcc -c -x assembler-with-cpp' instead. +# Under Irix 6, you will have to specify the ABI (-o32, -n32, or -64) +# if you use something other than the default ABI on your machine. + +CFLAGS= -O -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DNO_EXECUTE_PERMISSION -DALL_INTERIOR_POINTERS -DSILENT + +# For dynamic library builds, it may be necessary to add flags to generate +# PIC code, e.g. -fPIC on Linux. + +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. +# -DSILENT disables statistics printing, and improves performance. +# -DFIND_LEAK causes GC_find_leak to be initially set. +# This causes the collector to assume that all inaccessible +# objects should have been explicitly deallocated, and reports exceptions. +# Finalization and the test program are not usable in this mode. +# -DSOLARIS_THREADS enables support for Solaris (thr_) threads. +# (Clients should also define SOLARIS_THREADS and then include +# gc.h before performing thr_ or dl* or GC_ operations.) +# Must also define -D_REENTRANT. +# -D_SOLARIS_PTHREADS enables support for Solaris pthreads. +# Define SOLARIS_THREADS as well. +# -DIRIX_THREADS enables support for Irix pthreads. See README.irix. +# -DLINUX_THREADS enables support for Xavier Leroy's Linux threads. +# see README.linux. -D_REENTRANT may also be required. +# -DALL_INTERIOR_POINTERS allows all pointers to the interior +# of objects to be recognized. (See gc_priv.h for consequences.) +# -DSMALL_CONFIG tries to tune the collector for small heap sizes, +# usually causing it to use less space in such situations. +# Incremental collection no longer works in this case. +# -DLARGE_CONFIG tunes the collector for unusually large heaps. +# Necessary for heaps larger than about 500 MB on most machines. +# Recommended for heaps larger than about 64 MB. +# -DDONT_ADD_BYTE_AT_END is meaningful only with +# -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS +# causes all objects to be padded so that pointers just past the end of +# an object can be recognized. This can be expensive. (The padding +# is normally more than one byte due to alignment constraints.) +# -DDONT_ADD_BYTE_AT_END disables the padding. +# -DNO_SIGNALS does not disable signals during critical parts of +# the GC process. This is no less correct than many malloc +# implementations, and it sometimes has a significant performance +# impact. However, it is dangerous for many not-quite-ANSI C +# programs that call things like printf in asynchronous signal handlers. +# -DNO_EXECUTE_PERMISSION may cause some or all of the heap to not +# have execute permission, i.e. it may be impossible to execute +# code from the heap. Currently this only affects the incremental +# collector on UNIX machines. It may greatly improve its performance, +# since this may avoid some expensive cache synchronization. +# -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +# new syntax "operator new[]" for allocating and deleting arrays. +# See gc_cpp.h for details. No effect on the C part of the collector. +# This is defined implicitly in a few environments. +# -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be defined +# as aliases for X, GC_realloc, and GC_free, respectively. +# Calloc is redefined in terms of the new malloc. X should +# be either GC_malloc or GC_malloc_uncollectable. +# The former is occasionally useful for working around leaks in code +# you don't want to (or can't) look at. It may not work for +# existing code, but it often does. Neither works on all platforms, +# since some ports use malloc or calloc to obtain system memory. +# (Probably works for UNIX, and win32.) +# -DIGNORE_FREE turns calls to free into a noop. Only useful with +# -DREDIRECT_MALLOC. +# -DNO_DEBUGGING removes GC_dump and the debugging routines it calls. +# Reduces code size slightly at the expense of debuggability. +# -DJAVA_FINALIZATION makes it somewhat safer to finalize objects out of +# order by specifying a nonstandard finalization mark procedure (see +# finalize.c). Objects reachable from finalizable objects will be marked +# in a sepearte postpass, and hence their memory won't be reclaimed. +# Not recommended unless you are implementing a language that specifies +# these semantics. Since 5.0, determines only only the initial value +# of GC_java_finalization variable. +# -DFINALIZE_ON_DEMAND causes finalizers to be run only in response +# to explicit GC_invoke_finalizers() calls. +# In 5.0 this became runtime adjustable, and this only determines the +# initial value of GC_finalize_on_demand. +# -DATOMIC_UNCOLLECTABLE includes code for GC_malloc_atomic_uncollectable. +# This is useful if either the vendor malloc implementation is poor, +# or if REDIRECT_MALLOC is used. +# -DHBLKSIZE=ddd, where ddd is a power of 2 between 512 and 16384, explicitly +# sets the heap block size. Each heap block is devoted to a single size and +# kind of object. For the incremental collector it makes sense to match +# the most likely page size. Otherwise large values result in more +# fragmentation, but generally better performance for large heaps. +# -DUSE_MMAP use MMAP instead of sbrk to get new memory. +# Works for Solaris and Irix. +# -DUSE_MUNMAP causes memory to be returned to the OS under the right +# circumstances. This currently disables VM-based incremental collection. +# This is currently experimental, and works only under some Unix and +# Linux versions. +# -DMMAP_STACKS (for Solaris threads) Use mmap from /dev/zero rather than +# GC_scratch_alloc() to get stack memory. +# -DPRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever +# the garbage collector detects a value that looks almost, but not quite, +# like a pointer, print both the address containing the value, and the +# value of the near-bogus-pointer. Can be used to identifiy regions of +# memory that are likely to contribute misidentified pointers. +# -DOLD_BLOCK_ALLOC Use the old, possibly faster, large block +# allocation strategy. The new strategy tries harder to minimize +# fragmentation, sometimes at the expense of spending more time in the +# large block allocator and/or collecting more frequently. +# If you expect the allocator to promptly use an explicitly expanded +# heap, this is highly recommended. +# -DKEEP_BACK_PTRS Add code to save back pointers in debugging headers +# for objects allocated with the debugging allocator. If all objects +# through GC_MALLOC with GC_DEBUG defined, this allows the client +# to determine how particular or randomly chosen objects are reachable +# for debugging/profiling purposes. The backptr.h interface is +# implemented only if this is defined. +# -DGC_ASSERTIONS Enable some internal GC assertion checking. Currently +# this facility is only used in a few places. It is intended primarily +# for debugging of the garbage collector itself, but could also +# occasionally be useful for debugging of client code. Slows down the +# collector somewhat, but not drastically. +# -DCHECKSUMS reports on erroneously clear dirty bits, and unexpectedly +# altered stubborn objects, at substantial performance cost. +# Use only for debugging of the incremental collector. +# + + +LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \ + -DREDIRECT_MALLOC=GC_malloc_uncollectable \ + -DDONT_ADD_BYTE_AT_END -DALL_INTERIOR_POINTERS +# Flags for building libgc.a -- the last two are required. + +CXXFLAGS= $(CFLAGS) +AR= ar +RANLIB= ranlib + + +# Redefining srcdir allows object code for the nonPCR version of the collector +# to be generated in different directories. In this case, the destination directory +# should contain a copy of the original include directory. +srcdir = . +VPATH = $(srcdir) + +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o + +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c + +CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c cord/cord.h cord/ec.h cord/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC cord/SCOPTIONS.amiga cord/SMakefile.amiga + +CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o + +SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ + sparc_mach_dep.s gc.h gc_typed.h gc_hdrs.h gc_priv.h gc_private.h \ + gcconfig.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ + threadlibs.c if_mach.c if_not_there.c gc_cpp.cc gc_cpp.h weakpointer.h \ + gcc_support.c mips_ultrix_mach_dep.s include/gc_alloc.h gc_alloc.h \ + include/new_gc_alloc.h include/javaxfc.h sparc_sunos4_mach_dep.s \ + solaris_threads.h backptr.h $(CORD_SRCS) + +OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ + README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \ + SCoptions.amiga README.amiga README.win32 cord/README \ + cord/gc.h include/gc.h include/gc_typed.h include/cord.h \ + include/ec.h include/private/cord_pos.h include/private/gcconfig.h \ + include/private/gc_hdrs.h include/private/gc_priv.h \ + include/gc_cpp.h README.rs6000 include/backptr.h \ + include/weakpointer.h README.QUICK callprocs pc_excludes \ + barrett_diagram README.OS2 README.Mac MacProjects.sit.hqx \ + MacOS.c EMX_MAKEFILE makefile.depend README.debugging \ + include/gc_cpp.h Mac_files/datastart.c Mac_files/dataend.c \ + Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ + add_gc_prefix.c README.solaris2 README.sgi README.hp README.uts \ + win32_threads.c NT_THREADS_MAKEFILE gc.mak README.dj Makefile.dj \ + README.alpha README.linux version.h Makefile.DLLs \ + WCC_MAKEFILE nursery.c nursery.h gc_copy_descr.h \ + include/leak_detector.h + +CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \ + $(srcdir)/cord/private/cord_pos.h + +UTILS= if_mach if_not_there threadlibs + +# Libraries needed for curses applications. Only needed for de. +CURSES= -lcurses -ltermlib + +# The following is irrelevant on most systems. But a few +# versions of make otherwise fork the shell specified in +# the SHELL environment variable. +SHELL= /bin/sh + +SPECIALCFLAGS = +# Alternative flags to the C compiler for mach_dep.c. +# Mach_dep.c often doesn't like optimization, and it's +# not time-critical anyway. +# Set SPECIALCFLAGS to -q nodirect_code on Encore. + +all: gc.a gctest + +pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h gcconfig.h mach_dep.o $(SRCS) + make -f PCR-Makefile depend + make -f PCR-Makefile + +$(OBJS) test.o dyn_load.o dyn_load_sunos53.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h Makefile +# The dependency on Makefile is needed. Changing +# options such as -DSILENT affects the size of GC_arrays, +# invalidating all .o files that rely on gc_priv.h + +mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h + +base_lib gc.a: $(OBJS) dyn_load.o $(UTILS) + echo > base_lib + rm -f dont_ar_1 + ./if_mach SPARC SUNOS5 touch dont_ar_1 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(OBJS) dyn_load.o + ./if_mach M68K AMIGA touch dont_ar_1 + ./if_mach M68K AMIGA $(AR) -vrus gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 $(AR) ru gc.a $(OBJS) dyn_load.o + ./if_not_there dont_ar_1 $(RANLIB) gc.a || cat /dev/null +# ignore ranlib failure; that usually means it doesn't exist, and isn't needed + +cords: $(CORD_OBJS) cord/cordtest $(UTILS) + rm -f dont_ar_3 + ./if_mach SPARC SUNOS5 touch dont_ar_3 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(CORD_OBJS) + ./if_mach M68K AMIGA touch dont_ar_3 + ./if_mach M68K AMIGA $(AR) -vrus gc.a $(CORD_OBJS) + ./if_not_there dont_ar_3 $(AR) ru gc.a $(CORD_OBJS) + ./if_not_there dont_ar_3 $(RANLIB) gc.a || cat /dev/null + +gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h $(srcdir)/gc.h Makefile + $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc + +test_cpp: $(srcdir)/test_cpp.cc $(srcdir)/gc_cpp.h gc_cpp.o $(srcdir)/gc.h \ +base_lib $(UTILS) + rm -f test_cpp + ./if_mach HP_PA "" $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a -ldld + ./if_not_there test_cpp $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a `./threadlibs` + +c++: gc_cpp.o $(srcdir)/gc_cpp.h test_cpp + rm -f dont_ar_4 + ./if_mach SPARC SUNOS5 touch dont_ar_4 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a gc_cpp.o + ./if_mach M68K AMIGA touch dont_ar_4 + ./if_mach M68K AMIGA $(AR) -vrus gc.a gc_cpp.o + ./if_not_there dont_ar_4 $(AR) ru gc.a gc_cpp.o + ./if_not_there dont_ar_4 $(RANLIB) gc.a || cat /dev/null + ./test_cpp 1 + echo > c++ + +dyn_load_sunos53.o: dyn_load.c + $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ + +# SunOS5 shared library version of the collector +sunos5gc.so: $(OBJS) dyn_load_sunos53.o + $(CC) -G -o sunos5gc.so $(OBJS) dyn_load_sunos53.o -ldl + ln sunos5gc.so libgc.so + +# Alpha/OSF shared library version of the collector +libalphagc.so: $(OBJS) + ld -shared -o libalphagc.so $(OBJS) dyn_load.o -lc + ln libalphagc.so libgc.so + +# IRIX shared library version of the collector +libirixgc.so: $(OBJS) dyn_load.o + ld -shared $(ABI_FLAG) -o libirixgc.so $(OBJS) dyn_load.o -lc + ln libirixgc.so libgc.so + +# Linux shared library version of the collector +liblinuxgc.so: $(OBJS) dyn_load.o + gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o -lo + ln liblinuxgc.so libgc.so + +# Alternative Linux rule. This is preferable, but is likely to break the +# Makefile for some non-linux platforms. +# LIBOBJS= $(patsubst %.o, %.lo, $(OBJS)) +# +#.SUFFIXES: .lo $(SUFFIXES) +# +#.c.lo: +# $(CC) $(CFLAGS) $(CPPFLAGS) -fPIC -c $< -o $@ +# +# liblinuxgc.so: $(LIBOBJS) dyn_load.lo +# gcc -shared -Wl,-soname=libgc.so.0 -o libgc.so.0 $(LIBOBJS) dyn_load.lo +# touch liblinuxgc.so + +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s $(srcdir)/mips_ultrix_mach_dep.s $(srcdir)/rs6000_mach_dep.s $(UTILS) + rm -f mach_dep.o + ./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_mach_dep.s + ./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s + ./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s + ./if_mach RS6000 "" $(AS) -o mach_dep.o $(srcdir)/rs6000_mach_dep.s + ./if_mach ALPHA "" $(AS) -o mach_dep.o $(srcdir)/alpha_mach_dep.s + ./if_mach SPARC SUNOS5 $(AS) -o mach_dep.o $(srcdir)/sparc_mach_dep.s + ./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s + ./if_mach SPARC OPENBSD $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s + ./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + +mark_rts.o: $(srcdir)/mark_rts.c if_mach if_not_there $(UTILS) + rm -f mark_rts.o + -./if_mach ALPHA OSF1 $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c + ./if_not_there mark_rts.o $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c +# Work-around for DEC optimizer tail recursion elimination bug. +# The ALPHA-specific line should be removed if gcc is used. + +alloc.o: version.h + +cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordbscs.c + mv cordbscs.o cord/cordbscs.o +# not all compilers understand -o filename + +cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordxtra.c + mv cordxtra.o cord/cordxtra.o + +cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordprnt.c + mv cordprnt.o cord/cordprnt.o + +cord/cordtest: $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a $(UTILS) + rm -f cord/cordtest + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -lucb + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -ldld + ./if_not_there cord/cordtest $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a `./threadlibs` + +cord/de: $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(UTILS) + rm -f cord/de + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lucb `./threadlibs` + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -ldld + ./if_mach RS6000 "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` + ./if_mach ALPHA LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_mach M68K AMIGA $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_not_there cord/de $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) `./threadlibs` + +if_mach: $(srcdir)/if_mach.c $(srcdir)/gcconfig.h + $(CC) $(CFLAGS) -o if_mach $(srcdir)/if_mach.c + +threadlibs: $(srcdir)/threadlibs.c $(srcdir)/gcconfig.h Makefile + $(CC) $(CFLAGS) -o threadlibs $(srcdir)/threadlibs.c + +if_not_there: $(srcdir)/if_not_there.c + $(CC) $(CFLAGS) -o if_not_there $(srcdir)/if_not_there.c + +clean: + rm -f gc.a *.o gctest gctest_dyn_link test_cpp \ + setjmp_test mon.out gmon.out a.out core if_not_there if_mach \ + threadlibs $(CORD_OBJS) cord/cordtest cord/de + -rm -f *~ + +gctest: test.o gc.a if_mach if_not_there + rm -f gctest + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest test.o gc.a -lucb + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o gctest test.o gc.a -ldld + ./if_not_there gctest $(CC) $(CFLAGS) -o gctest test.o gc.a `./threadlibs` + +# If an optimized setjmp_test generates a segmentation fault, +# odds are your compiler is broken. Gctest may still work. +# Try compiling setjmp_t.c unoptimized. +setjmp_test: $(srcdir)/setjmp_t.c $(srcdir)/gc.h if_mach if_not_there + $(CC) $(CFLAGS) -o setjmp_test $(srcdir)/setjmp_t.c + +test: KandRtest cord/cordtest + cord/cordtest + +# Those tests that work even with a K&R C compiler: +KandRtest: setjmp_test gctest + ./setjmp_test + ./gctest + +add_gc_prefix: add_gc_prefix.c + $(CC) -o add_gc_prefix $(srcdir)/add_gc_prefix.c + +gc.tar: $(SRCS) $(OTHER_FILES) add_gc_prefix + ./add_gc_prefix $(SRCS) $(OTHER_FILES) > /tmp/gc.tar-files + (cd $(srcdir)/.. ; tar cvfh - `cat /tmp/gc.tar-files`) > gc.tar + +pc_gc.tar: $(SRCS) $(OTHER_FILES) + tar cvfX pc_gc.tar pc_excludes $(SRCS) $(OTHER_FILES) + +floppy: pc_gc.tar + -mmd a:/cord + -mmd a:/cord/private + -mmd a:/include + -mmd a:/include/private + mkdir /tmp/pc_gc + cat pc_gc.tar | (cd /tmp/pc_gc; tar xvf -) + -mcopy -tmn /tmp/pc_gc/* a: + -mcopy -tmn /tmp/pc_gc/cord/* a:/cord + -mcopy -mn /tmp/pc_gc/cord/de_win.ICO a:/cord + -mcopy -tmn /tmp/pc_gc/cord/private/* a:/cord/private + -mcopy -tmn /tmp/pc_gc/include/* a:/include + -mcopy -tmn /tmp/pc_gc/include/private/* a:/include/private + rm -r /tmp/pc_gc + +gc.tar.Z: gc.tar + compress gc.tar + +gc.tar.gz: gc.tar + gzip gc.tar + +lint: $(CSRCS) test.c + lint -DLINT $(CSRCS) test.c | egrep -v "possible pointer alignment problem|abort|exit|sbrk|mprotect|syscall|change in ANSI|improper alignment" + +# BTL: added to test shared library version of collector. +# Currently works only under SunOS5. Requires GC_INIT call from statically +# loaded client code. +ABSDIR = `pwd` +gctest_dyn_link: test.o libgc.so + $(CC) -L$(ABSDIR) -R$(ABSDIR) -o gctest_dyn_link test.o -lgc -ldl -lthread + +gctest_irix_dyn_link: test.o libirixgc.so + $(CC) -L$(ABSDIR) -o gctest_irix_dyn_link test.o -lirixgc + +test_dll.o: test.c libgc_globals.h + $(CC) $(CFLAGS) -DGC_USE_DLL -c test.c -o test_dll.o + +test_dll: test_dll.o libgc_dll.a libgc.dll + $(CC) test_dll.o -L$(ABSDIR) -lgc_dll -o test_dll + +SYM_PREFIX-libgc=GC + +# Uncomment the following line to build a GNU win32 DLL +# include Makefile.DLLs + +reserved_namespace: $(SRCS) + for file in $(SRCS) test.c test_cpp.cc; do \ + sed s/GC_/_GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done + +user_namespace: $(SRCS) + for file in $(SRCS) test.c test_cpp.cc; do \ + sed s/_GC_/GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done diff --git a/gc/Makefile.DLLs b/gc/Makefile.DLLs new file mode 100644 index 0000000..011f49d --- /dev/null +++ b/gc/Makefile.DLLs @@ -0,0 +1,107 @@ +#-----------------------------------------------------------------------------# + +# Makefile.DLLs, version 0.4. + +# Contributed by Fergus Henderson. + +# This Makefile contains rules for creating DLLs on Windows using gnu-win32. + +#-----------------------------------------------------------------------------# + +# This rule creates a `.def' file, which lists the symbols that are exported +# from the DLL. We use `nm' to get a list of all the exported text (`T') +# symbols and data symbols -- including uninitialized data (`B'), +# initialized data (`D'), read-only data (`R'), and common blocks (`C'). +%.def: %.a + echo EXPORTS > $@ + nm $< | grep '^........ [BCDRT] _' | sed 's/[^_]*_//' >> $@ + +# We need to use macros to access global data: +# the user of the DLL must refer to `foo' as `(*__imp_foo)'. +# This rule creates a `_globals.h' file, which contains macros +# for doing this. + +SYM_PREFIX = $(firstword $(SYM_PREFIX-$*) $*) +DLL_MACRO = $(SYM_PREFIX)_USE_DLL +IMP_MACRO = $(SYM_PREFIX)_IMP +GLOBAL_MACRO = $(SYM_PREFIX)_GLOBAL + +%_globals.h: %.a + echo "/* automatically generated by Makefile.DLLs */" > $@ + echo "#if defined(__GNUC__) && defined(_WIN32) \\" >> $@ + echo " && defined($(DLL_MACRO))" >> $@ + echo "# define $(IMP_MACRO)(name) __imp_##name" >> $@ + echo "# define $(GLOBAL_MACRO)(name) (*$(IMP_MACRO)(name))" >> $@ + echo "#else" >> $@ + echo "# define $(GLOBAL_MACRO)(name) name" >> $@ + echo "#endif" >> $@ + echo "" >> $@ + for sym in `nm $< | grep '^........ [BCDR] _' | sed 's/[^_]*_//'`; do \ + echo "#define $$sym $(GLOBAL_MACRO)($$sym)" >> $@; \ + done + +# This rule creates the export object file (`foo.exp') which contains the +# jump table array; this export object file becomes part of the DLL. +# This rule also creates the import library (`foo_dll.a') which contains small +# stubs for all the functions exported by the DLL which jump to them via the +# jump table. Executables that will use the DLL must be linked against this +# stub library. +%.exp %_dll.a : %.def + dlltool $(DLLTOOLFLAGS) $(DLLTOOLFLAGS-$*) \ + --def $< \ + --dllname $*.dll \ + --output-exp $*.exp \ + --output-lib $*_dll.a + +# The `sed' commands below are to convert DOS-style `C:\foo\bar' +# pathnames into Unix-style `//c/foo/bar' pathnames. +CYGWIN32_LIBS = $(shell echo \ + -L`dirname \`gcc -print-file-name=libgcc.a | \ + sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \ + -L`dirname \`gcc -print-file-name=libcygwin.a | \ + sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \ + -L`dirname \`gcc -print-file-name=libkernel32.a | \ + sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \ + -lgcc -lcygwin -lkernel32 -lgcc) + +RELOCATABLE=yes + +ifeq "$(strip $(RELOCATABLE))" "yes" + +# to create relocatable DLLs, we need to do two passes +%.dll: %.exp %.a dll_fixup.o dll_init.o + $(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll -o $*.base \ + -e _dll_entry@12 dll_init.o \ + dll_fixup.o $*.exp $*.a \ + $(LDLIBS) $(LDLIBS-$*) \ + $(CYGWIN32_LIBS) + $(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll --base-file $*.base -o $@ \ + -e _dll_entry@12 dll_init.o \ + dll_fixup.o $*.exp $*.a \ + $(LDLIBS) $(LDLIBS-$*) \ + $(CYGWIN32_LIBS) + rm -f $*.base +else + +%.dll: %.exp %.a dll_fixup.o dll_init.o + $(LD) $(LDFLAGS) $(LDFLAGS-$*) --dll -o $@ \ + -e _dll_entry@12 dll_init.o \ + dll_fixup.o $*.exp $*.a \ + $(LDLIBS) $(LDLIBS-$*) \ + $(CYGWIN32_LIBS) + +endif + +# This black magic piece of assembler needs to be linked in in order to +# properly terminate the list of imported DLLs. +dll_fixup.s: + echo '.section .idata$$3' > dll_fixup.s + echo '.long 0,0,0,0, 0,0,0,0' >> dll_fixup.s + +# This bit is necessary to provide an initialization function for the DLL. +dll_init.c: + echo '__attribute__((stdcall))' > dll_init.c + echo 'int dll_entry(int handle, int reason, void *ptr)' >> dll_init.c + echo '{return 1; }' >> dll_init.c + +dont_throw_away: dll_fixup.o dll_init.o diff --git a/gc/Makefile.dj b/gc/Makefile.dj new file mode 100644 index 0000000..54f77db --- /dev/null +++ b/gc/Makefile.dj @@ -0,0 +1,436 @@ +# Primary targets: +# gc.a - builds basic library +# libgc.a - builds library for use with g++ "-fgc-keyword" extension +# c++ - adds C++ interface to library +# cords - adds cords (heavyweight strings) to library +# test - prints porting information, then builds basic version of gc.a, +# and runs some tests of collector and cords. Does not add cords or +# c++ interface to gc.a +# cord/de$(EXE_SUFFIX) - builds dumb editor based on cords. +ABI_FLAG= +CC=gcc $(ABI_FLAG) +CXX=gxx $(ABI_FLAG) +AS=gcc -c -x assembler-with-cpp $(ABI_FLAG) +# The above doesn't work with gas, which doesn't run cpp. +# Define AS as `gcc -c -x assembler-with-cpp' instead. +# Under Irix 6, you will have to specify the ABI (-o32, -n32, or -64) +# if you use something other than the default ABI on your machine. + +# special defines for DJGPP +CXXLD=gxx $(ABI_FLAG) +EXE_SUFFIX=.exe + +CFLAGS= -O -DATOMIC_UNCOLLECTABLE -DNO_SIGNALS -DALL_INTERIOR_POINTERS -DNO_EXECUTE_PERMISSION -DSILENT + +# For dynamic library builds, it may be necessary to add flags to generate +# PIC code, e.g. -fPIC on Linux. + +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. +# -DSILENT disables statistics printing, and improves performance. +# -DCHECKSUMS reports on erroneously clear dirty bits, and unexpectedly +# altered stubborn objects, at substantial performance cost. +# Use only for incremental collector debugging. +# -DFIND_LEAK causes the collector to assume that all inaccessible +# objects should have been explicitly deallocated, and reports exceptions. +# Finalization and the test program are not usable in this mode. +# -DSOLARIS_THREADS enables support for Solaris (thr_) threads. +# (Clients should also define SOLARIS_THREADS and then include +# gc.h before performing thr_ or dl* or GC_ operations.) +# Must also define -D_REENTRANT. +# -D_SOLARIS_PTHREADS enables support for Solaris pthreads. +# Define SOLARIS_THREADS as well. +# -DIRIX_THREADS enables support for Irix pthreads. See README.irix. +# -DLINUX_THREADS enables support for Xavier Leroy's Linux threads. +# see README.linux. -D_REENTRANT may also be required. +# -DALL_INTERIOR_POINTERS allows all pointers to the interior +# of objects to be recognized. (See gc_priv.h for consequences.) +# -DSMALL_CONFIG tries to tune the collector for small heap sizes, +# usually causing it to use less space in such situations. +# Incremental collection no longer works in this case. +# -DLARGE_CONFIG tunes the collector for unusually large heaps. +# Necessary for heaps larger than about 500 MB on most machines. +# Recommended for heaps larger than about 64 MB. +# -DDONT_ADD_BYTE_AT_END is meaningful only with +# -DALL_INTERIOR_POINTERS. Normally -DALL_INTERIOR_POINTERS +# causes all objects to be padded so that pointers just past the end of +# an object can be recognized. This can be expensive. (The padding +# is normally more than one byte due to alignment constraints.) +# -DDONT_ADD_BYTE_AT_END disables the padding. +# -DNO_SIGNALS does not disable signals during critical parts of +# the GC process. This is no less correct than many malloc +# implementations, and it sometimes has a significant performance +# impact. However, it is dangerous for many not-quite-ANSI C +# programs that call things like printf in asynchronous signal handlers. +# -DNO_EXECUTE_PERMISSION may cause some or all of the heap to not +# have execute permission, i.e. it may be impossible to execute +# code from the heap. Currently this only affects the incremental +# collector on UNIX machines. It may greatly improve its performance, +# since this may avoid some expensive cache synchronization. +# -DOPERATOR_NEW_ARRAY declares that the C++ compiler supports the +# new syntax "operator new[]" for allocating and deleting arrays. +# See gc_cpp.h for details. No effect on the C part of the collector. +# This is defined implicitly in a few environments. +# -DREDIRECT_MALLOC=X causes malloc, realloc, and free to be defined +# as aliases for X, GC_realloc, and GC_free, respectively. +# Calloc is redefined in terms of the new malloc. X should +# be either GC_malloc or GC_malloc_uncollectable. +# The former is occasionally useful for working around leaks in code +# you don't want to (or can't) look at. It may not work for +# existing code, but it often does. Neither works on all platforms, +# since some ports use malloc or calloc to obtain system memory. +# (Probably works for UNIX, and win32.) +# -DIGNORE_FREE turns calls to free into a noop. Only useful with +# -DREDIRECT_MALLOC. +# -DNO_DEBUGGING removes GC_dump and the debugging routines it calls. +# Reduces code size slightly at the expense of debuggability. +# -DJAVA_FINALIZATION makes it somewhat safer to finalize objects out of +# order by specifying a nonstandard finalization mark procedure (see +# finalize.c). Objects reachable from finalizable objects will be marked +# in a sepearte postpass, and hence their memory won't be reclaimed. +# Not recommended unless you are implementing a language that specifies +# these semantics. +# -DFINALIZE_ON_DEMAND causes finalizers to be run only in response +# to explicit GC_invoke_finalizers() calls. +# -DATOMIC_UNCOLLECTABLE includes code for GC_malloc_atomic_uncollectable. +# This is useful if either the vendor malloc implementation is poor, +# or if REDIRECT_MALLOC is used. +# -DHBLKSIZE=ddd, where ddd is a power of 2 between 512 and 16384, explicitly +# sets the heap block size. Each heap block is devoted to a single size and +# kind of object. For the incremental collector it makes sense to match +# the most likely page size. Otherwise large values result in more +# fragmentation, but generally better performance for large heaps. +# -DUSE_MMAP use MMAP instead of sbrk to get new memory. +# Works for Solaris and Irix. +# -DMMAP_STACKS (for Solaris threads) Use mmap from /dev/zero rather than +# GC_scratch_alloc() to get stack memory. +# -DPRINT_BLACK_LIST Whenever a black list entry is added, i.e. whenever +# the garbage collector detects a value that looks almost, but not quite, +# like a pointer, print both the address containing the value, and the +# value of the near-bogus-pointer. Can be used to identifiy regions of +# memory that are likely to contribute misidentified pointers. +# -DOLD_BLOCK_ALLOC Use the old, possibly faster, large block +# allocation strategy. The new strategy tries harder to minimize +# fragmentation, sometimes at the expense of spending more time in the +# large block allocator and/or collecting more frequently. +# If you expect the allocator to promtly use an explicitly expanded +# heap, this is highly recommended. +# + + + +LIBGC_CFLAGS= -O -DNO_SIGNALS -DSILENT \ + -DREDIRECT_MALLOC=GC_malloc_uncollectable \ + -DDONT_ADD_BYTE_AT_END -DALL_INTERIOR_POINTERS +# Flags for building libgc.a -- the last two are required. + +CXXFLAGS= $(CFLAGS) -DOPERATOR_NEW_ARRAY +AR= ar +RANLIB= ranlib + + +# Redefining srcdir allows object code for the nonPCR version of the collector +# to be generated in different directories. In this case, the destination directory +# should contain a copy of the original include directory. +srcdir = . +VPATH = $(srcdir) + +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o irix_threads.o linux_threads.o typd_mlc.o ptr_chck.o mallocx.o solaris_pthreads.o + +CSRCS= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c irix_threads.c linux_threads.c typd_mlc.c ptr_chck.c mallocx.c solaris_pthreads.c + +CORD_SRCS= cord/cordbscs.c cord/cordxtra.c cord/cordprnt.c cord/de.c cord/cordtest.c cord/cord.h cord/ec.h cord/private/cord_pos.h cord/de_win.c cord/de_win.h cord/de_cmds.h cord/de_win.ICO cord/de_win.RC cord/SCOPTIONS.amiga cord/SMakefile.amiga + +CORD_OBJS= cord/cordbscs.o cord/cordxtra.o cord/cordprnt.o + +SRCS= $(CSRCS) mips_sgi_mach_dep.s rs6000_mach_dep.s alpha_mach_dep.s \ + sparc_mach_dep.s gc.h gc_typed.h gc_hdrs.h gc_priv.h gc_private.h \ + gcconfig.h gc_mark.h include/gc_inl.h include/gc_inline.h gc.man \ + threadlibs.c if_mach.c if_not_there.c gc_cpp.cc gc_cpp.h weakpointer.h \ + gcc_support.c mips_ultrix_mach_dep.s include/gc_alloc.h gc_alloc.h \ + include/new_gc_alloc.h include/javaxfc.h sparc_sunos4_mach_dep.s \ + solaris_threads.h $(CORD_SRCS) + +OTHER_FILES= Makefile PCR-Makefile OS2_MAKEFILE NT_MAKEFILE BCC_MAKEFILE \ + README test.c test_cpp.cc setjmp_t.c SMakefile.amiga \ + SCoptions.amiga README.amiga README.win32 cord/README \ + cord/gc.h include/gc.h include/gc_typed.h include/cord.h \ + include/ec.h include/private/cord_pos.h include/private/gcconfig.h \ + include/private/gc_hdrs.h include/private/gc_priv.h \ + include/gc_cpp.h README.rs6000 \ + include/weakpointer.h README.QUICK callprocs pc_excludes \ + barrett_diagram README.OS2 README.Mac MacProjects.sit.hqx \ + MacOS.c EMX_MAKEFILE makefile.depend README.debugging \ + include/gc_cpp.h Mac_files/datastart.c Mac_files/dataend.c \ + Mac_files/MacOS_config.h Mac_files/MacOS_Test_config.h \ + add_gc_prefix.c README.solaris2 README.sgi README.hp README.uts \ + win32_threads.c NT_THREADS_MAKEFILE gc.mak README.dj Makefile.dj \ + README.alpha README.linux version.h Makefile.DLLs \ + WCC_MAKEFILE + +CORD_INCLUDE_FILES= $(srcdir)/gc.h $(srcdir)/cord/cord.h $(srcdir)/cord/ec.h \ + $(srcdir)/cord/private/cord_pos.h + +UTILS= if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX) + +# Libraries needed for curses applications. Only needed for de. +CURSES= -lcurses -ltermlib + +# The following is irrelevant on most systems. But a few +# versions of make otherwise fork the shell specified in +# the SHELL environment variable. +SHELL= /bin/sh + +SPECIALCFLAGS = +# Alternative flags to the C compiler for mach_dep.c. +# Mach_dep.c often doesn't like optimization, and it's +# not time-critical anyway. +# Set SPECIALCFLAGS to -q nodirect_code on Encore. + +all: gc.a gctest$(EXE_SUFFIX) + +pcr: PCR-Makefile gc_private.h gc_hdrs.h gc.h gcconfig.h mach_dep.o $(SRCS) + make -f PCR-Makefile depend + make -f PCR-Makefile + +$(OBJS) test.o dyn_load.o dyn_load_sunos53.o: $(srcdir)/gc_priv.h $(srcdir)/gc_hdrs.h $(srcdir)/gc.h \ + $(srcdir)/gcconfig.h $(srcdir)/gc_typed.h Makefile +# The dependency on Makefile is needed. Changing +# options such as -DSILENT affects the size of GC_arrays, +# invalidating all .o files that rely on gc_priv.h + +mark.o typd_mlc.o finalize.o: $(srcdir)/gc_mark.h + +base_lib gc.a: $(OBJS) dyn_load.o $(UTILS) + echo > base_lib + rm -f on_sparc_sunos5_1 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_1 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(OBJS) dyn_load.o + ./if_not_there on_sparc_sunos5_1 $(AR) ru gc.a $(OBJS) dyn_load.o + -./if_not_there on_sparc_sunos5_1 $(RANLIB) gc.a +# ignore ranlib failure; that usually means it doesn't exist, and isn't needed + +cords: $(CORD_OBJS) cord/cordtest$(EXE_SUFFIX) $(UTILS) + rm -f on_sparc_sunos5_3 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_3 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a $(CORD_OBJS) + ./if_not_there on_sparc_sunos5_3 $(AR) ru gc.a $(CORD_OBJS) + -./if_not_there on_sparc_sunos5_3 $(RANLIB) gc.a + +gc_cpp.o: $(srcdir)/gc_cpp.cc $(srcdir)/gc_cpp.h $(srcdir)/gc.h Makefile + $(CXX) -c $(CXXFLAGS) $(srcdir)/gc_cpp.cc + +test_cpp$(EXE_SUFFIX): $(srcdir)/test_cpp.cc $(srcdir)/gc_cpp.h gc_cpp.o $(srcdir)/gc.h \ +base_lib $(UTILS) + rm -f test_cpp test_cpp$(EXE_SUFFIX) + ./if_mach HP_PA "" $(CXX) $(CXXFLAGS) -o test_cpp $(srcdir)/test_cpp.cc gc_cpp.o gc.a -ldld + ./if_not_there test_cpp$(EXE_SUFFIX) $(CXXLD) $(CXXFLAGS) -o test_cpp$(EXE_SUFFIX) $(srcdir)/test_cpp.cc gc_cpp.o gc.a + rm -f test_cpp + +c++: gc_cpp.o $(srcdir)/gc_cpp.h test_cpp$(EXE_SUFFIX) + rm -f on_sparc_sunos5_4 + ./if_mach SPARC SUNOS5 touch on_sparc_sunos5_4 + ./if_mach SPARC SUNOS5 $(AR) rus gc.a gc_cpp.o + ./if_not_there on_sparc_sunos5_4 $(AR) ru gc.a gc_cpp.o + -./if_not_there on_sparc_sunos5_4 $(RANLIB) gc.a + ./test_cpp$(EXE_SUFFIX) 1 + echo > c++ + +dyn_load_sunos53.o: dyn_load.c + $(CC) $(CFLAGS) -DSUNOS53_SHARED_LIB -c $(srcdir)/dyn_load.c -o $@ + +# SunOS5 shared library version of the collector +sunos5gc.so: $(OBJS) dyn_load_sunos53.o + $(CC) -G -o sunos5gc.so $(OBJS) dyn_load_sunos53.o -ldl + ln sunos5gc.so libgc.so + +# Alpha/OSF shared library version of the collector +libalphagc.so: $(OBJS) + ld -shared -o libalphagc.so $(OBJS) dyn_load.o -lc + ln libalphagc.so libgc.so + +# IRIX shared library version of the collector +libirixgc.so: $(OBJS) dyn_load.o + ld -shared $(ABI_FLAG) -o libirixgc.so $(OBJS) dyn_load.o -lc + ln libirixgc.so libgc.so + +# Linux shared library version of the collector +liblinuxgc.so: $(OBJS) dyn_load.o + gcc -shared -o liblinuxgc.so $(OBJS) dyn_load.o -lo + ln liblinuxgc.so libgc.so + +mach_dep.o: $(srcdir)/mach_dep.c $(srcdir)/mips_sgi_mach_dep.s $(srcdir)/mips_ultrix_mach_dep.s $(srcdir)/rs6000_mach_dep.s $(UTILS) + rm -f mach_dep.o + ./if_mach MIPS IRIX5 $(AS) -o mach_dep.o $(srcdir)/mips_sgi_mach_dep.s + ./if_mach MIPS RISCOS $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s + ./if_mach MIPS ULTRIX $(AS) -o mach_dep.o $(srcdir)/mips_ultrix_mach_dep.s + ./if_mach RS6000 "" $(AS) -o mach_dep.o $(srcdir)/rs6000_mach_dep.s + ./if_mach ALPHA "" $(AS) -o mach_dep.o $(srcdir)/alpha_mach_dep.s + ./if_mach SPARC SUNOS5 $(AS) -o mach_dep.o $(srcdir)/sparc_mach_dep.s + ./if_mach SPARC SUNOS4 $(AS) -o mach_dep.o $(srcdir)/sparc_sunos4_mach_dep.s + ./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) $(srcdir)/mach_dep.c + +mark_rts.o: $(srcdir)/mark_rts.c if_mach if_not_there $(UTILS) + rm -f mark_rts.o + -./if_mach ALPHA OSF1 $(CC) -c $(CFLAGS) -Wo,-notail $(srcdir)/mark_rts.c + ./if_not_there mark_rts.o $(CC) -c $(CFLAGS) $(srcdir)/mark_rts.c +# Work-around for DEC optimizer tail recursion elimination bug. +# The ALPHA-specific line should be removed if gcc is used. + +alloc.o: version.h + +cord/cordbscs.o: $(srcdir)/cord/cordbscs.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordbscs.c + mv cordbscs.o cord/cordbscs.o +# not all compilers understand -o filename + +cord/cordxtra.o: $(srcdir)/cord/cordxtra.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordxtra.c + mv cordxtra.o cord/cordxtra.o + +cord/cordprnt.o: $(srcdir)/cord/cordprnt.c $(CORD_INCLUDE_FILES) + $(CC) $(CFLAGS) -c -I$(srcdir) $(srcdir)/cord/cordprnt.c + mv cordprnt.o cord/cordprnt.o + +cord/cordtest$(EXE_SUFFIX): $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a $(UTILS) /tmp + rm -f cord/cordtest$(EXE_SUFFIX) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/cordtest$(EXE_SUFFIX) $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -lucb + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/cordtest$(EXE_SUFFIX) $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a -ldld + ./if_not_there cord/cordtest$(EXE_SUFFIX) $(CC) $(CFLAGS) -o cord/cordtest $(srcdir)/cord/cordtest.c $(CORD_OBJS) gc.a + rm -f cord/cordtest cordtest + -mv cordtest$(EXE_SUFFIX) cord/ + +/tmp: $(UTILS) + ./if_not_there /tmp mkdir /tmp + +cord/de$(EXE_SUFFIX): $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(UTILS) + rm -f cord/de cord/de$(EXE_SUFFIX) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -lucb `./threadlibs` + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) -ldld + ./if_mach RS6000 "" $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_mach I386 LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses `./threadlibs` + ./if_mach ALPHA LINUX $(CC) $(CFLAGS) -o cord/de $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a -lcurses + ./if_not_there cord/de$(EXE_SUFFIX) $(CC) $(CFLAGS) -o cord/de$(EXE_SUFFIX) $(srcdir)/cord/de.c cord/cordbscs.o cord/cordxtra.o gc.a $(CURSES) + +if_mach$(EXE_SUFFIX): $(srcdir)/if_mach.c $(srcdir)/gcconfig.h + rm -f if_mach if_mach$(EXE_SUFFIX) + $(CC) $(CFLAGS) -o if_mach $(srcdir)/if_mach.c + rm -f if_mach + +threadlibs$(EXE_SUFFIX): $(srcdir)/threadlibs.c $(srcdir)/gcconfig.h Makefile + rm -f threadlibs threadlibs$(EXE_SUFFIX) + $(CC) $(CFLAGS) -o threadlibs $(srcdir)/threadlibs.c + rm -f threadlibs + +if_not_there$(EXE_SUFFIX): $(srcdir)/if_not_there.c + rm -f if_not_there if_not_there$(EXE_SUFFIX) + $(CC) $(CFLAGS) -o if_not_there $(srcdir)/if_not_there.c + rm -f if_not_there + +# Clean removes *.o several times, +# because as the first one doesn't seem to get them all! +clean: + rm -f gc.a *.o + rm -f *.o + rm -f *.o + rm -f cord/*.o + rm -f gctest gctest_dyn_link test_cpp + rm -f setjmp_test mon.out gmon.out a.out core if_not_there if_mach + rm -f threadlibs $(CORD_OBJS) cordtest cord/cordtest de cord/de + rm -f gctest$(EXE_SUFFIX) gctest_dyn_link$(EXE_SUFFIX) test_cpp$(EXE_SUFFIX) + rm -f setjmp_test$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX) if_mach$(EXE_SUFFIX) + rm -f threadlibs$(EXE_SUFFIX) cord/cordtest$(EXE_SUFFIX) + -rm -f *~ + +gctest$(EXE_SUFFIX): test.o gc.a if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX) + rm -f gctest gctest$(EXE_SUFFIX) + ./if_mach SPARC DRSNX $(CC) $(CFLAGS) -o gctest test.o gc.a -lucb + ./if_mach HP_PA "" $(CC) $(CFLAGS) -o gctest test.o gc.a -ldld + ./if_not_there gctest$(EXE_SUFFIX) $(CC) $(CFLAGS) -o gctest$(EXE_SUFFIX) test.o gc.a + rm -f gctest + +# If an optimized setjmp_test generates a segmentation fault, +# odds are your compiler is broken. Gctest may still work. +# Try compiling setjmp_t.c unoptimized. +setjmp_test$(EXE_SUFFIX): $(srcdir)/setjmp_t.c $(srcdir)/gc.h \ + if_mach$(EXE_SUFFIX) if_not_there$(EXE_SUFFIX) + rm -f setjmp_test$(EXE_SUFFIX) + $(CC) $(CFLAGS) -o setjmp_test $(srcdir)/setjmp_t.c + rm -f setjmp_test + +test: KandRtest cord/cordtest$(EXE_SUFFIX) + ./cord/cordtest$(EXE_SUFFIX) + +# Those tests that work even with a K&R C compiler: +KandRtest: setjmp_test$(EXE_SUFFIX) gctest$(EXE_SUFFIX) + ./setjmp_test$(EXE_SUFFIX) + ./gctest$(EXE_SUFFIX) + +add_gc_prefix$(EXE_SUFFIX): add_gc_prefix.c + $(CC) -o add_gc_prefix$(EXE_SUFFIX) $(srcdir)/add_gc_prefix.c + rm -f add_gc_prefix + +gc.tar: $(SRCS) $(OTHER_FILES) add_gc_prefix + ./add_gc_prefix$(EXE_SUFFIX) $(SRCS) $(OTHER_FILES) > /tmp/gc.tar-files + (cd $(srcdir)/.. ; tar cvfh - `cat /tmp/gc.tar-files`) > gc.tar + +pc_gc.tar: $(SRCS) $(OTHER_FILES) + tar cvfX pc_gc.tar pc_excludes $(SRCS) $(OTHER_FILES) + +floppy: pc_gc.tar + -mmd a:/cord + -mmd a:/cord/private + -mmd a:/include + -mmd a:/include/private + mkdir /tmp/pc_gc + cat pc_gc.tar | (cd /tmp/pc_gc; tar xvf -) + -mcopy -tmn /tmp/pc_gc/* a: + -mcopy -tmn /tmp/pc_gc/cord/* a:/cord + -mcopy -mn /tmp/pc_gc/cord/de_win.ICO a:/cord + -mcopy -tmn /tmp/pc_gc/cord/private/* a:/cord/private + -mcopy -tmn /tmp/pc_gc/include/* a:/include + -mcopy -tmn /tmp/pc_gc/include/private/* a:/include/private + rm -r /tmp/pc_gc + +gc.tar.Z: gc.tar + compress gc.tar + +gc.tar.gz: gc.tar + gzip gc.tar + +lint: $(CSRCS) test.c + lint -DLINT $(CSRCS) test.c | egrep -v "possible pointer alignment problem|abort|exit|sbrk|mprotect|syscall" + +# BTL: added to test shared library version of collector. +# Currently works only under SunOS5. Requires GC_INIT call from statically +# loaded client code. +ABSDIR = `pwd` +gctest_dyn_link: test.o libgc.so + $(CC) -L$(ABSDIR) -R$(ABSDIR) -o gctest_dyn_link test.o -lgc -ldl -lthread + +gctest_irix_dyn_link: test.o libirixgc.so + $(CC) -L$(ABSDIR) -o gctest_irix_dyn_link test.o -lirixgc + +test_dll.o: test.c libgc_globals.h + $(CC) $(CFLAGS) -DGC_USE_DLL -c test.c -o test_dll.o + +test_dll: test_dll.o libgc_dll.a libgc.dll + $(CC) test_dll.o -L$(ABSDIR) -lgc_dll -o test_dll + +SYM_PREFIX-libgc=GC + +# Uncomment the following line to build a GNU win32 DLL +# include Makefile.DLLs + +reserved_namespace: $(SRCS) + for file in $(SRCS) test.c test_cpp.cc; do \ + sed s/GC_/_GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done + +user_namespace: $(SRCS) + for file in $(SRCS) test.c test_cpp.cc; do \ + sed s/_GC_/GC_/g < $$file > tmp; \ + cp tmp $$file; \ + done + diff --git a/gc/NT_MAKEFILE b/gc/NT_MAKEFILE new file mode 100644 index 0000000..52f6f4a --- /dev/null +++ b/gc/NT_MAKEFILE @@ -0,0 +1,59 @@ +# Makefile for Windows NT. Assumes Microsoft compiler, and a single thread. +# DLLs are included in the root set under NT, but not under win32S. +# Use "nmake nodebug=1 all" for optimized versions of library, gctest and editor. + +CPU= i386 +!include <ntwin32.mak> + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj malloc.obj stubborn.obj dyn_load.obj typd_mlc.obj ptr_chck.obj gc_cpp.obj mallocx.obj + +all: gctest.exe cord\de.exe test_cpp.exe + +.c.obj: + $(cc) $(cdebug) $(cflags) $(cvars) -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS -D__STDC__ $*.c /Fo$*.obj + +.cpp.obj: + $(cc) $(cdebug) $(cflags) $(cvars) -DSMALL_CONFIG -DSILENT -DALL_INTERIOR_POINTERS $*.CPP /Fo$*.obj + +$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h + +gc.lib: $(OBJS) + lib /MACHINE:i386 /out:gc.lib $(OBJS) +# The original NT SDK used lib32 instead of lib + +gctest.exe: test.obj gc.lib +# The following works for win32 debugging. For win32s debugging use debugtype:coff +# and add mapsympe line. +# This produces a "GUI" applications that opens no windows and writes to the log file +# "gc.log". This is done to make the result runnable under win32s. + $(link) -debug:full -debugtype:cv $(guiflags) -stack:131072 -out:$*.exe test.obj $(guilibs) gc.lib +# mapsympe -n -o gctest.sym gctest.exe + +cord\de_win.rbj: cord\de_win.res + cvtres -$(CPU) cord\de_win.res -o cord\de_win.rbj + +cord\de.obj cord\de_win.obj: cord\cord.h cord\private\cord_pos.h cord\de_win.h cord\de_cmds.h + +cord\de_win.res: cord\de_win.rc cord\de_win.h cord\de_cmds.h + $(rc) $(rcvars) -r -fo cord\de_win.res $(cvars) cord\de_win.rc + +# Cord/de is a real win32 gui application. +cord\de.exe: cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj cord\de_win.rbj gc.lib + $(link) -debug:full -debugtype:cv $(guiflags) -stack:16384 -out:cord\de.exe cord\cordbscs.obj cord\cordxtra.obj cord\de.obj cord\de_win.obj cord\de_win.rbj gc.lib $(guilibs) + +gc_cpp.obj: gc_cpp.h gc.h + +gc_cpp.cpp: gc_cpp.cc + copy gc_cpp.cc gc_cpp.cpp + +test_cpp.cpp: test_cpp.cc + copy test_cpp.cc test_cpp.cpp + +# This generates the C++ test executable. The executable expects +# a single numeric argument, which is the number of iterations. +# The output appears in the file "gc.log". +test_cpp.exe: test_cpp.obj gc_cpp.h gc.h gc.lib + $(link) -debug:full -debugtype:cv $(guiflags) -stack:16384 -out:test_cpp.exe test_cpp.obj gc.lib $(guilibs) + + + diff --git a/gc/OS2_MAKEFILE b/gc/OS2_MAKEFILE new file mode 100644 index 0000000..7b81621 --- /dev/null +++ b/gc/OS2_MAKEFILE @@ -0,0 +1,45 @@ +# Makefile for OS/2. Assumes IBM's compiler, static linking, and a single thread. +# Adding dynamic linking support seems easy, but takes a little bit of work. +# Adding thread support may be nontrivial, since we haven't yet figured out how to +# look at another thread's registers. + +# Significantly revised for GC version 4.4 by Mark Boulter (Jan 1994). + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj obj_map.obj blacklst.obj finalize.obj new_hblk.obj dbg_mlc.obj malloc.obj stubborn.obj typd_mlc.obj ptr_chck.obj mallocx.obj + +CORDOBJS= cord\cordbscs.obj cord\cordxtra.obj cord\cordprnt.obj + +CC= icc +CFLAGS= /O /Q /DSILENT /DSMALL_CONFIG /DALL_INTERIOR_POINTERS +# Use /Ti instead of /O for debugging +# Setjmp_test may yield overly optimistic results when compiled +# without optimization. + +all: $(OBJS) gctest.exe cord\cordtest.exe + +$(OBJS) test.obj: gc_priv.h gc_hdrs.h gc.h + +## ERASE THE LIB FIRST - if it is already there then this command will fail +## (make sure its there or erase will fail!) +gc.lib: $(OBJS) + echo . > gc.lib + erase gc.lib + LIB gc.lib $(OBJS), gc.lst + +mach_dep.obj: mach_dep.c + $(CC) $(CFLAGS) /C mach_dep.c + +gctest.exe: test.obj gc.lib + $(CC) $(CFLAGS) /B"/STACK:524288" /Fegctest test.obj gc.lib + +cord\cordbscs.obj: cord\cordbscs.c cord\cord.h cord\private\cord_pos.h + $(CC) $(CFLAGS) /C /Focord\cordbscs cord\cordbscs.c + +cord\cordxtra.obj: cord\cordxtra.c cord\cord.h cord\private\cord_pos.h cord\ec.h + $(CC) $(CFLAGS) /C /Focord\cordxtra cord\cordxtra.c + +cord\cordprnt.obj: cord\cordprnt.c cord\cord.h cord\private\cord_pos.h cord\ec.h + $(CC) $(CFLAGS) /C /Focord\cordprnt cord\cordprnt.c + +cord\cordtest.exe: cord\cordtest.c cord\cord.h cord\private\cord_pos.h cord\ec.h $(CORDOBJS) gc.lib + $(CC) $(CFLAGS) /B"/STACK:65536" /Fecord\cordtest cord\cordtest.c gc.lib $(CORDOBJS) diff --git a/gc/PCR-Makefile b/gc/PCR-Makefile new file mode 100644 index 0000000..1eae367 --- /dev/null +++ b/gc/PCR-Makefile @@ -0,0 +1,68 @@ +# +# Default target +# + +default: gc.o + +include ../config/common.mk + +# +# compilation flags, etc. +# + + +CPPFLAGS = $(INCLUDE) $(CONFIG_CPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR +#CFLAGS = -DPCR -DSILENT $(CONFIG_CFLAGS) +CFLAGS = -DPCR $(CONFIG_CFLAGS) +SPECIALCFLAGS = # For code involving asm's + +ASPPFLAGS = $(INCLUDE) $(CONFIG_ASPPFLAGS) \ + -DPCR_NO_RENAME -DPCR_NO_HOSTDEP_ERR -DASM + +ASFLAGS = $(CONFIG_ASFLAGS) + +LDRFLAGS = $(CONFIG_LDRFLAGS) + +LDFLAGS = $(CONFIG_LDFLAGS) + +# +# +# +# +# BEGIN PACKAGE-SPECIFIC PART +# +# +# +# + +# Fix to point to local pcr installation directory. +PCRDIR= .. + +COBJ= alloc.o reclaim.o allchblk.o misc.o os_dep.o mark_rts.o headers.o mark.o obj_map.o pcr_interface.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o solaris_threads.o typd_mlc.o ptr_chck.o mallocx.o + +CSRC= reclaim.c allchblk.c misc.c alloc.c mach_dep.c os_dep.c mark_rts.c headers.c mark.c obj_map.c pcr_interface.c blacklst.c finalize.c new_hblk.c real_malloc.c dyn_load.c dbg_mlc.c malloc.c stubborn.c checksums.c solaris_threads.c typd_mlc.c ptr_chck.c mallocx.c + +SHELL= /bin/sh + +default: gc.o + +gc.o: $(COBJ) mach_dep.o + $(LDR) $(CONFIG_LDRFLAGS) -o gc.o $(COBJ) mach_dep.o + + +mach_dep.o: mach_dep.c mips_mach_dep.s rs6000_mach_dep.s if_mach if_not_there + rm -f mach_dep.o + ./if_mach MIPS "" as -o mach_dep.o mips_mach_dep.s + ./if_mach RS6000 "" as -o mach_dep.o rs6000_mach_dep.s + ./if_mach ALPHA "" as -o mach_dep.o alpha_mach_dep.s + ./if_mach SPARC SUNOS5 as -o mach_dep.o sparc_mach_dep.s + ./if_not_there mach_dep.o $(CC) -c $(SPECIALCFLAGS) mach_dep.c + +if_mach: if_mach.c gcconfig.h + $(CC) $(CFLAGS) -o if_mach if_mach.c + +if_not_there: if_not_there.c + $(CC) $(CFLAGS) -o if_not_there if_not_there.c + + diff --git a/gc/README b/gc/README new file mode 100644 index 0000000..80cb26a --- /dev/null +++ b/gc/README @@ -0,0 +1,1517 @@ +Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers +Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. +Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +This is version 5.0alpha3 of a conservative garbage collector for C and C++. + +You might find a more recent version of this at + +http://www.hpl.hp.com/personal/Hans_Boehm/gc + +HISTORY - + + Early versions of this collector were developed as a part of research +projects supported in part by the National Science Foundation +and the Defense Advance Research Projects Agency. +Much of the code was rewritten by Hans-J. Boehm (boehm@acm.org) at Xerox PARC +and at SGI. + +Some other contributors: + +More recent contributors are mentioned in the modification history at the +end of this file. My apologies for any omissions. + +The SPARC specific code was contributed by Mark Weiser +(weiser@parc.xerox.com). The Encore Multimax modifications were supplied by +Kevin Kenny (kenny@m.cs.uiuc.edu). The adaptation to the RT is largely due +to Vernon Lee (scorpion@rice.edu), on machines made available by IBM. +Much of the HP specific code and a number of good suggestions for improving the +generic code are due to Walter Underwood (wunder@hp-ses.sde.hp.com). +Robert Brazile (brazile@diamond.bbn.com) originally supplied the ULTRIX code. +Al Dosser (dosser@src.dec.com) and Regis Cridlig (Regis.Cridlig@cl.cam.ac.uk) +subsequently provided updates and information on variation between ULTRIX +systems. Parag Patel (parag@netcom.com) supplied the A/UX code. +Jesper Peterson(jep@mtiame.mtia.oz.au), Michel Schinz, and +Martin Tauchmann (martintauchmann@bigfoot.com) supplied the Amiga port. +Thomas Funke (thf@zelator.in-berlin.de(?)) and +Brian D.Carlstrom (bdc@clark.lcs.mit.edu) supplied the NeXT ports. +Douglas Steel (doug@wg.icl.co.uk) provided ICL DRS6000 code. +Bill Janssen (janssen@parc.xerox.com) supplied the SunOS dynamic loader +specific code. Manuel Serrano (serrano@cornas.inria.fr) supplied linux and +Sony News specific code. Al Dosser provided Alpha/OSF/1 code. He and +Dave Detlefs(detlefs@src.dec.com) also provided several generic bug fixes. +Alistair G. Crooks(agc@uts.amdahl.com) supplied the NetBSD and 386BSD ports. +Jeffrey Hsu (hsu@soda.berkeley.edu) provided the FreeBSD port. +Brent Benson (brent@jade.ssd.csd.harris.com) ported the collector to +a Motorola 88K processor running CX/UX (Harris NightHawk). +Ari Huttunen (Ari.Huttunen@hut.fi) generalized the OS/2 port to +nonIBM development environments (a nontrivial task). +Patrick Beard (beard@cs.ucdavis.edu) provided the initial MacOS port. +David Chase, then at Olivetti Research, suggested several improvements. +Scott Schwartz (schwartz@groucho.cse.psu.edu) supplied some of the +code to save and print call stacks for leak detection on a SPARC. +Jesse Hull and John Ellis supplied the C++ interface code. +Zhong Shao performed much of the experimentation that led to the +current typed allocation facility. (His dynamic type inference code hasn't +made it into the released version of the collector, yet.) +(Blame for misinstallation of these modifications goes to the first author, +however.) + +OVERVIEW + + This is intended to be a general purpose, garbage collecting storage +allocator. The algorithms used are described in: + +Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment", +Software Practice & Experience, September 1988, pp. 807-820. + +Boehm, H., A. Demers, and S. Shenker, "Mostly Parallel Garbage Collection", +Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design +and Implementation, SIGPLAN Notices 26, 6 (June 1991), pp. 157-164. + +Boehm, H., "Space Efficient Conservative Garbage Collection", Proceedings +of the ACM SIGPLAN '91 Conference on Programming Language Design and +Implementation, SIGPLAN Notices 28, 6 (June 1993), pp. 197-206. + + Possible interactions between the collector and optimizing compilers are +discussed in + +Boehm, H., and D. Chase, "A Proposal for GC-safe C Compilation", +The Journal of C Language Translation 4, 2 (December 1992). + +and + +Boehm H., "Simple GC-safe Compilation", Proceedings +of the ACM SIGPLAN '96 Conference on Programming Language Design and +Implementation. + +(Both are also available from +http://reality.sgi.com/boehm/papers/, among other places.) + + Unlike the collector described in the second reference, this collector +operates either with the mutator stopped during the entire collection +(default) or incrementally during allocations. (The latter is supported +on only a few machines.) It does not rely on threads, but is intended +to be thread-safe. + + Some of the ideas underlying the collector have previously been explored +by others. (Doug McIlroy wrote a vaguely similar collector that is part of +version 8 UNIX (tm).) However none of this work appears to have been widely +disseminated. + + Rudimentary tools for use of the collector as a leak detector are included, as +is a fairly sophisticated string package "cord" that makes use of the collector. +(See cord/README.) + + +GENERAL DESCRIPTION + + This is a garbage collecting storage allocator that is intended to be +used as a plug-in replacement for C's malloc. + + Since the collector does not require pointers to be tagged, it does not +attempt to ensure that all inaccessible storage is reclaimed. However, +in our experience, it is typically more successful at reclaiming unused +memory than most C programs using explicit deallocation. Unlike manually +introduced leaks, the amount of unreclaimed memory typically stays +bounded. + + In the following, an "object" is defined to be a region of memory allocated +by the routines described below. + + Any objects not intended to be collected must be pointed to either +from other such accessible objects, or from the registers, +stack, data, or statically allocated bss segments. Pointers from +the stack or registers may point to anywhere inside an object. +The same is true for heap pointers if the collector is compiled with + ALL_INTERIOR_POINTERS defined, as is now the default. + +Compiling without ALL_INTERIOR_POINTERS may reduce accidental retention +of garbage objects, by requiring pointers from the heap to to the beginning +of an object. But this no longer appears to be a significant +issue for most programs. + +There are a number of routines which modify the pointer recognition +algorithm. GC_register_displacement allows certain interior pointers +to be recognized even if ALL_INTERIOR_POINTERS is nor defined. +GC_malloc_ignore_off_page allows some pointers into the middle of large objects +to be disregarded, greatly reducing the probablility of accidental +retention of large objects. For most purposes it seems best to compile +with ALL_INTERIOR_POINTERS and to use GC_malloc_ignore_off_page if +you get collector warnings from allocations of very large objects. +See README.debugging for details. + + Note that pointers inside memory allocated by the standard "malloc" are not +seen by the garbage collector. Thus objects pointed to only from such a +region may be prematurely deallocated. It is thus suggested that the +standard "malloc" be used only for memory regions, such as I/O buffers, that +are guaranteed not to contain pointers to garbage collectable memory. +Pointers in C language automatic, static, or register variables, +are correctly recognized. (Note that GC_malloc_uncollectable has semantics +similar to standard malloc, but allocates objects that are traced by the +collector.) + + The collector does not always know how to find pointers in data +areas that are associated with dynamic libraries. This is easy to +remedy IF you know how to find those data areas on your operating +system (see GC_add_roots). Code for doing this under SunOS, IRIX 5.X and 6.X, +HP/UX, Alpha OSF/1, Linux, and win32 is included and used by default. (See +README.win32 for win32 details.) On other systems pointers from dynamic +library data areas may not be considered by the collector. + + Note that the garbage collector does not need to be informed of shared +read-only data. However if the shared library mechanism can introduce +discontiguous data areas that may contain pointers, then the collector does +need to be informed. + + Signal processing for most signals may be deferred during collection, +and during uninterruptible parts of the allocation process. Unlike +standard ANSI C mallocs, it can be safe to invoke malloc +from a signal handler while another malloc is in progress, provided +the original malloc is not restarted. (Empirically, many UNIX +applications already assume this.) To obtain this level of signal +safety, remove the definition of -DNO_SIGNALS in Makefile. This incurs +a minor performance penalty, and hence is no longer the default. + + The allocator/collector can also be configured for thread-safe operation. +(Full signal safety can also be achieved, but only at the cost of two system +calls per malloc, which is usually unacceptable.) + +INSTALLATION AND PORTABILITY + + As distributed, the macro SILENT is defined in Makefile. +In the event of problems, this can be removed to obtain a moderate +amount of descriptive output for each collection. +(The given statistics exhibit a few peculiarities. +Things don't appear to add up for a variety of reasons, most notably +fragmentation losses. These are probably much more significant for the +contrived program "test.c" than for your application.) + + Note that typing "make test" will automatically build the collector +and then run setjmp_test and gctest. Setjmp_test will give you information +about configuring the collector, which is useful primarily if you have +a machine that's not already supported. Gctest is a somewhat superficial +test of collector functionality. Failure is indicated by a core dump or +a message to the effect that the collector is broken. Gctest takes about +35 seconds to run on a SPARCstation 2. On a slower machine, +expect it to take a while. It may use up to 8 MB of memory. (The +multi-threaded version will use more.) "Make test" will also, as +its last step, attempt to build and test the "cord" string library. +This will fail without an ANSI C compiler. + + The Makefile will generate a library gc.a which you should link against. +Typing "make cords" will add the cord library to gc.a. +Note that this requires an ANSI C compiler. + + It is suggested that if you need to replace a piece of the collector +(e.g. GC_mark_rts.c) you simply list your version ahead of gc.a on the + work.) +ld command line, rather than replacing the one in gc.a. (This will +generate numerous warnings under some versions of AIX, but it still +works.) + + All include files that need to be used by clients will be put in the +include subdirectory. (Normally this is just gc.h. "Make cords" adds +"cord.h" and "ec.h".) + + The collector currently is designed to run essentially unmodified on +machines that use a flat 32-bit or 64-bit address space. +That includes the vast majority of Workstations and X86 (X >= 3) PCs. +(The list here was deleted because it was getting too long and constantly +out of date.) + It does NOT run under plain 16-bit DOS or Windows 3.X. There are however +various packages (e.g. win32s, djgpp) that allow flat 32-bit address +applications to run under those systemsif the have at least an 80386 processor, +and several of those are compatible with the collector. + + In a few cases (Amiga, OS/2, Win32, MacOS) a separate makefile +or equivalent is supplied. Many of these have separate README.system +files. + + Dynamic libraries are completely supported only under SunOS +(and even that support is not functional on the last Sun 3 release), +IRIX 5&6, HP-PA, Win32 (not Win32S) and OSF/1 on DEC AXP machines. +On other machines we recommend that you do one of the following: + + 1) Add dynamic library support (and send us the code). + 2) Use static versions of the libraries. + 3) Arrange for dynamic libraries to use the standard malloc. + This is still dangerous if the library stores a pointer to a + garbage collected object. But nearly all standard interfaces + prohibit this, because they deal correctly with pointers + to stack allocated objects. (Strtok is an exception. Don't + use it.) + + In all cases we assume that pointer alignment is consistent with that +enforced by the standard C compilers. If you use a nonstandard compiler +you may have to adjust the alignment parameters defined in gc_priv.h. + + A port to a machine that is not byte addressed, or does not use 32 bit +or 64 bit addresses will require a major effort. A port to plain MSDOS +or win16 is hard. + + For machines not already mentioned, or for nonstandard compilers, the +following are likely to require change: + +1. The parameters in gcconfig.h. + The parameters that will usually require adjustment are + STACKBOTTOM, ALIGNMENT and DATASTART. Setjmp_test + prints its guesses of the first two. + DATASTART should be an expression for computing the + address of the beginning of the data segment. This can often be + &etext. But some memory management units require that there be + some unmapped space between the text and the data segment. Thus + it may be more complicated. On UNIX systems, this is rarely + documented. But the adb "$m" command may be helpful. (Note + that DATASTART will usually be a function of &etext. Thus a + single experiment is usually insufficient.) + STACKBOTTOM is used to initialize GC_stackbottom, which + should be a sufficient approximation to the coldest stack address. + On some machines, it is difficult to obtain such a value that is + valid across a variety of MMUs, OS releases, etc. A number of + alternatives exist for using the collector in spite of this. See the + discussion in gcconfig.h immediately preceding the various + definitions of STACKBOTTOM. + +2. mach_dep.c. + The most important routine here is one to mark from registers. + The distributed file includes a generic hack (based on setjmp) that + happens to work on many machines, and may work on yours. Try + compiling and running setjmp_t.c to see whether it has a chance of + working. (This is not correct C, so don't blame your compiler if it + doesn't work. Based on limited experience, register window machines + are likely to cause trouble. If your version of setjmp claims that + all accessible variables, including registers, have the value they + had at the time of the longjmp, it also will not work. Vanilla 4.2 BSD + on Vaxen makes such a claim. SunOS does not.) + If your compiler does not allow in-line assembly code, or if you prefer + not to use such a facility, mach_dep.c may be replaced by a .s file + (as we did for the MIPS machine and the PC/RT). + At this point enough architectures are supported by mach_dep.c + that you will rarely need to do more than adjust for assembler + syntax. + +3. os_dep.c (and gc_priv.h). + Several kinds of operating system dependent routines reside here. + Many are optional. Several are invoked only through corresponding + macros in gc_priv.h, which may also be redefined as appropriate. + The routine GC_register_data_segments is crucial. It registers static + data areas that must be traversed by the collector. (User calls to + GC_add_roots may sometimes be used for similar effect.) + Routines to obtain memory from the OS also reside here. + Alternatively this can be done entirely by the macro GET_MEM + defined in gc_priv.h. Routines to disable and reenable signals + also reside here if they are need by the macros DISABLE_SIGNALS + and ENABLE_SIGNALS defined in gc_priv.h. + In a multithreaded environment, the macros LOCK and UNLOCK + in gc_priv.h will need to be suitably redefined. + The incremental collector requires page dirty information, which + is acquired through routines defined in os_dep.c. Unless directed + otherwise by gcconfig.h, these are implemented as stubs that simply + treat all pages as dirty. (This of course makes the incremental + collector much less useful.) + +4. dyn_load.c + This provides a routine that allows the collector to scan data + segments associated with dynamic libraries. Often it is not + necessary to provide this routine unless user-written dynamic + libraries are used. + + For a different version of UN*X or different machines using the +Motorola 68000, Vax, SPARC, 80386, NS 32000, PC/RT, or MIPS architecture, +it should frequently suffice to change definitions in gcconfig.h. + + +THE C INTERFACE TO THE ALLOCATOR + + The following routines are intended to be directly called by the user. +Note that usually only GC_malloc is necessary. GC_clear_roots and GC_add_roots +calls may be required if the collector has to trace from nonstandard places +(e.g. from dynamic library data areas on a machine on which the +collector doesn't already understand them.) On some machines, it may +be desirable to set GC_stacktop to a good approximation of the stack base. +(This enhances code portability on HP PA machines, since there is no +good way for the collector to compute this value.) Client code may include +"gc.h", which defines all of the following, plus many others. + +1) GC_malloc(nbytes) + - allocate an object of size nbytes. Unlike malloc, the object is + cleared before being returned to the user. Gc_malloc will + invoke the garbage collector when it determines this to be appropriate. + GC_malloc may return 0 if it is unable to acquire sufficient + space from the operating system. This is the most probable + consequence of running out of space. Other possible consequences + are that a function call will fail due to lack of stack space, + or that the collector will fail in other ways because it cannot + maintain its internal data structures, or that a crucial system + process will fail and take down the machine. Most of these + possibilities are independent of the malloc implementation. + +2) GC_malloc_atomic(nbytes) + - allocate an object of size nbytes that is guaranteed not to contain any + pointers. The returned object is not guaranteed to be cleared. + (Can always be replaced by GC_malloc, but results in faster collection + times. The collector will probably run faster if large character + arrays, etc. are allocated with GC_malloc_atomic than if they are + statically allocated.) + +3) GC_realloc(object, new_size) + - change the size of object to be new_size. Returns a pointer to the + new object, which may, or may not, be the same as the pointer to + the old object. The new object is taken to be atomic iff the old one + was. If the new object is composite and larger than the original object, + then the newly added bytes are cleared (we hope). This is very likely + to allocate a new object, unless MERGE_SIZES is defined in gc_priv.h. + Even then, it is likely to recycle the old object only if the object + is grown in small additive increments (which, we claim, is generally bad + coding practice.) + +4) GC_free(object) + - explicitly deallocate an object returned by GC_malloc or + GC_malloc_atomic. Not necessary, but can be used to minimize + collections if performance is critical. Probably a performance + loss for very small objects (<= 8 bytes). + +5) GC_expand_hp(bytes) + - Explicitly increase the heap size. (This is normally done automatically + if a garbage collection failed to GC_reclaim enough memory. Explicit + calls to GC_expand_hp may prevent unnecessarily frequent collections at + program startup.) + +6) GC_malloc_ignore_off_page(bytes) + - identical to GC_malloc, but the client promises to keep a pointer to + the somewhere within the first 256 bytes of the object while it is + live. (This pointer should nortmally be declared volatile to prevent + interference from compiler optimizations.) This is the recommended + way to allocate anything that is likely to be larger than 100Kbytes + or so. (GC_malloc may result in failure to reclaim such objects.) + +7) GC_set_warn_proc(proc) + - Can be used to redirect warnings from the collector. Such warnings + should be rare, and should not be ignored during code development. + +8) GC_enable_incremental() + - Enables generational and incremental collection. Useful for large + heaps on machines that provide access to page dirty information. + Some dirty bit implementations may interfere with debugging + (by catching address faults) and place restrictions on heap arguments + to system calls (since write faults inside a system call may not be + handled well). + +9) Several routines to allow for registration of finalization code. + User supplied finalization code may be invoked when an object becomes + unreachable. To call (*f)(obj, x) when obj becomes inaccessible, use + GC_register_finalizer(obj, f, x, 0, 0); + For more sophisticated uses, and for finalization ordering issues, + see gc.h. + + The global variable GC_free_space_divisor may be adjusted up from its +default value of 4 to use less space and more collection time, or down for +the opposite effect. Setting it to 1 or 0 will effectively disable collections +and cause all allocations to simply grow the heap. + + The variable GC_non_gc_bytes, which is normally 0, may be changed to reflect +the amount of memory allocated by the above routines that should not be +considered as a candidate for collection. Careless use may, of course, result +in excessive memory consumption. + + Some additional tuning is possible through the parameters defined +near the top of gc_priv.h. + + If only GC_malloc is intended to be used, it might be appropriate to define: + +#define malloc(n) GC_malloc(n) +#define calloc(m,n) GC_malloc((m)*(n)) + + For small pieces of VERY allocation intensive code, gc_inl.h +includes some allocation macros that may be used in place of GC_malloc +and friends. + + All externally visible names in the garbage collector start with "GC_". +To avoid name conflicts, client code should avoid this prefix, except when +accessing garbage collector routines or variables. + + There are provisions for allocation with explicit type information. +This is rarely necessary. Details can be found in gc_typed.h. + +THE C++ INTERFACE TO THE ALLOCATOR: + + The Ellis-Hull C++ interface to the collector is included in +the collector distribution. If you intend to use this, type +"make c++" after the initial build of the collector is complete. +See gc_cpp.h for the definition of the interface. This interface +tries to approximate the Ellis-Detlefs C++ garbage collection +proposal without compiler changes. + +Cautions: +1. Arrays allocated without new placement syntax are +allocated as uncollectable objects. They are traced by the +collector, but will not be reclaimed. + +2. Failure to use "make c++" in combination with (1) will +result in arrays allocated using the default new operator. +This is likely to result in disaster without linker warnings. + +3. If your compiler supports an overloaded new[] operator, +then gc_cpp.cc and gc_cpp.h should be suitably modified. + +4. Many current C++ compilers have deficiencies that +break some of the functionality. See the comments in gc_cpp.h +for suggested workarounds. + +USE AS LEAK DETECTOR: + + The collector may be used to track down leaks in C programs that are +intended to run with malloc/free (e.g. code with extreme real-time or +portability constraints). To do so define FIND_LEAK in Makefile +This will cause the collector to invoke the report_leak +routine defined near the top of reclaim.c whenever an inaccessible +object is found that has not been explicitly freed. The collector will +no longer reclaim inaccessible memory; in this form it is purely a +debugging tool. + Productive use of this facility normally involves redefining report_leak +to do something more intelligent. This typically requires annotating +objects with additional information (e.g. creation time stack trace) that +identifies their origin. Such code is typically not very portable, and is +not included here, except on SPARC machines. + If all objects are allocated with GC_DEBUG_MALLOC (see next section), +then the default version of report_leak will report the source file +and line number at which the leaked object was allocated. This may +sometimes be sufficient. (On SPARC/SUNOS4 machines, it will also report +a cryptic stack trace. This can often be turned into a sympolic stack +trace by invoking program "foo" with "callprocs foo". Callprocs is +a short shell script that invokes adb to expand program counter values +to symbolic addresses. It was largely supplied by Scott Schwartz.) + Note that the debugging facilities described in the next section can +sometimes be slightly LESS effective in leak finding mode, since in +leak finding mode, GC_debug_free actually results in reuse of the object. +(Otherwise the object is simply marked invalid.) Also note that the test +program is not designed to run meaningfully in FIND_LEAK mode. +Use "make gc.a" to build the collector. + +DEBUGGING FACILITIES: + + The routines GC_debug_malloc, GC_debug_malloc_atomic, GC_debug_realloc, +and GC_debug_free provide an alternate interface to the collector, which +provides some help with memory overwrite errors, and the like. +Objects allocated in this way are annotated with additional +information. Some of this information is checked during garbage +collections, and detected inconsistencies are reported to stderr. + + Simple cases of writing past the end of an allocated object should +be caught if the object is explicitly deallocated, or if the +collector is invoked while the object is live. The first deallocation +of an object will clear the debugging info associated with an +object, so accidentally repeated calls to GC_debug_free will report the +deallocation of an object without debugging information. Out of +memory errors will be reported to stderr, in addition to returning +NIL. + + GC_debug_malloc checking during garbage collection is enabled +with the first call to GC_debug_malloc. This will result in some +slowdown during collections. If frequent heap checks are desired, +this can be achieved by explicitly invoking GC_gcollect, e.g. from +the debugger. + + GC_debug_malloc allocated objects should not be passed to GC_realloc +or GC_free, and conversely. It is however acceptable to allocate only +some objects with GC_debug_malloc, and to use GC_malloc for other objects, +provided the two pools are kept distinct. In this case, there is a very +low probablility that GC_malloc allocated objects may be misidentified as +having been overwritten. This should happen with probability at most +one in 2**32. This probability is zero if GC_debug_malloc is never called. + + GC_debug_malloc, GC_malloc_atomic, and GC_debug_realloc take two +additional trailing arguments, a string and an integer. These are not +interpreted by the allocator. They are stored in the object (the string is +not copied). If an error involving the object is detected, they are printed. + + The macros GC_MALLOC, GC_MALLOC_ATOMIC, GC_REALLOC, GC_FREE, and +GC_REGISTER_FINALIZER are also provided. These require the same arguments +as the corresponding (nondebugging) routines. If gc.h is included +with GC_DEBUG defined, they call the debugging versions of these +functions, passing the current file name and line number as the two +extra arguments, where appropriate. If gc.h is included without GC_DEBUG +defined, then all these macros will instead be defined to their nondebugging +equivalents. (GC_REGISTER_FINALIZER is necessary, since pointers to +objects with debugging information are really pointers to a displacement +of 16 bytes form the object beginning, and some translation is necessary +when finalization routines are invoked. For details, about what's stored +in the header, see the definition of the type oh in debug_malloc.c) + +INCREMENTAL/GENERATIONAL COLLECTION: + +The collector normally interrupts client code for the duration of +a garbage collection mark phase. This may be unacceptable if interactive +response is needed for programs with large heaps. The collector +can also run in a "generational" mode, in which it usually attempts to +collect only objects allocated since the last garbage collection. +Furthermore, in this mode, garbage collections run mostly incrementally, +with a small amount of work performed in response to each of a large number of +GC_malloc requests. + +This mode is enabled by a call to GC_enable_incremental(). + +Incremental and generational collection is effective in reducing +pause times only if the collector has some way to tell which objects +or pages have been recently modified. The collector uses two sources +of information: + +1. Information provided by the VM system. This may be provided in +one of several forms. Under Solaris 2.X (and potentially under other +similar systems) information on dirty pages can be read from the +/proc file system. Under other systems (currently SunOS4.X) it is +possible to write-protect the heap, and catch the resulting faults. +On these systems we require that system calls writing to the heap +(other than read) be handled specially by client code. +See os_dep.c for details. + +2. Information supplied by the programmer. We define "stubborn" +objects to be objects that are rarely changed. Such an object +can be allocated (and enabled for writing) with GC_malloc_stubborn. +Once it has been initialized, the collector should be informed with +a call to GC_end_stubborn_change. Subsequent writes that store +pointers into the object must be preceded by a call to +GC_change_stubborn. + +This mechanism performs best for objects that are written only for +initialization, and such that only one stubborn object is writable +at once. It is typically not worth using for short-lived +objects. Stubborn objects are treated less efficiently than pointerfree +(atomic) objects. + +A rough rule of thumb is that, in the absence of VM information, garbage +collection pauses are proportional to the amount of pointerful storage +plus the amount of modified "stubborn" storage that is reachable during +the collection. + +Initial allocation of stubborn objects takes longer than allocation +of other objects, since other data structures need to be maintained. + +We recommend against random use of stubborn objects in client +code, since bugs caused by inappropriate writes to stubborn objects +are likely to be very infrequently observed and hard to trace. +However, their use may be appropriate in a few carefully written +library routines that do not make the objects themselves available +for writing by client code. + + +BUGS: + + Any memory that does not have a recognizable pointer to it will be +reclaimed. Exclusive-or'ing forward and backward links in a list +doesn't cut it. + Some C optimizers may lose the last undisguised pointer to a memory +object as a consequence of clever optimizations. This has almost +never been observed in practice. Send mail to boehm@acm.org +for suggestions on how to fix your compiler. + This is not a real-time collector. In the standard configuration, +percentage of time required for collection should be constant across +heap sizes. But collection pauses will increase for larger heaps. +(On SPARCstation 2s collection times will be on the order of 300 msecs +per MB of accessible memory that needs to be scanned. Your mileage +may vary.) The incremental/generational collection facility helps, +but is portable only if "stubborn" allocation is used. + Please address bug reports to boehm@acm.org. If you are +contemplating a major addition, you might also send mail to ask whether +it's already been done (or whether we tried and discarded it). + +RECENT VERSIONS: + + Version 1.3 and immediately preceding versions contained spurious +assembly language assignments to TMP_SP. Only the assignment in the PC/RT +code is necessary. On other machines, with certain compiler options, +the assignments can lead to an unsaved register being overwritten. +Known to cause problems under SunOS 3.5 WITHOUT the -O option. (With +-O the compiler recognizes it as dead code. It probably shouldn't, +but that's another story.) + + Version 1.4 and earlier versions used compile time determined values +for the stack base. This no longer works on Sun 3s, since Sun 3/80s use +a different stack base. We now use a straightforward heuristic on all +machines on which it is known to work (incl. Sun 3s) and compile-time +determined values for the rest. There should really be library calls +to determine such values. + + Version 1.5 and earlier did not ensure 8 byte alignment for objects +allocated on a sparc based machine. + + Version 1.8 added ULTRIX support in gc_private.h. + + Version 1.9 fixed a major bug in gc_realloc. + + Version 2.0 introduced a consistent naming convention for collector +routines and added support for registering dynamic library data segments +in the standard mark_roots.c. Most of the data structures were revamped. +The treatment of interior pointers was completely changed. Finalization +was added. Support for locking was added. Object kinds were added. +We added a black listing facility to avoid allocating at addresses known +to occur as integers somewhere in the address space. Much of this +was accomplished by adapting ideas and code from the PCR collector. +The test program was changed and expanded. + + Version 2.1 was the first stable version since 1.9, and added support +for PPCR. + + Version 2.2 added debugging allocation, and fixed various bugs. Among them: +- GC_realloc could fail to extend the size of the object for certain large object sizes. +- A blatant subscript range error in GC_printf, which unfortunately + wasn't exercised on machines with sufficient stack alignment constraints. +- GC_register_displacement did the wrong thing if it was called after + any allocation had taken place. +- The leak finding code would eventually break after 2048 byte + byte objects leaked. +- interface.c didn't compile. +- The heap size remained much too small for large stacks. +- The stack clearing code behaved badly for large stacks, and perhaps + on HP/PA machines. + + Version 2.3 added ALL_INTERIOR_POINTERS and fixed the following bugs: +- Missing declaration of etext in the A/UX version. +- Some PCR root-finding problems. +- Blacklisting was not 100% effective, because the plausible future + heap bounds were being miscalculated. +- GC_realloc didn't handle out-of-memory correctly. +- GC_base could return a nonzero value for addresses inside free blocks. +- test.c wasn't really thread safe, and could erroneously report failure + in a multithreaded environment. (The locking primitives need to be + replaced for other threads packages.) +- GC_CONS was thoroughly broken. +- On a SPARC with dynamic linking, signals stayed diabled while the + client code was running. + (Thanks to Manuel Serrano at INRIA for reporting the last two.) + + Version 2.4 added GC_free_space_divisor as a tuning knob, added + support for OS/2 and linux, and fixed the following bugs: +- On machines with unaligned pointers (e.g. Sun 3), every 128th word could + fail to be considered for marking. +- Dynamic_load.c erroneously added 4 bytes to the length of the data and + bss sections of the dynamic library. This could result in a bad memory + reference if the actual length was a multiple of a page. (Observed on + Sun 3. Can probably also happen on a Sun 4.) + (Thanks to Robert Brazile for pointing out that the Sun 3 version + was broken. Dynamic library handling is still broken on Sun 3s + under 4.1.1U1, but apparently not 4.1.1. If you have such a machine, + use -Bstatic.) + + Version 2.5 fixed the following bugs: +- Removed an explicit call to exit(1) +- Fixed calls to GC_printf and GC_err_printf, so the correct number of + arguments are always supplied. The OS/2 C compiler gets confused if + the number of actuals and the number of formals differ. (ANSI C + doesn't require this to work. The ANSI sanctioned way of doing things + causes too many compatibility problems.) + + Version 3.0 added generational/incremental collection and stubborn + objects. + + Version 3.1 added the following features: +- A workaround for a SunOS 4.X SPARC C compiler + misfeature that caused problems when the collector was turned into + a dynamic library. +- A fix for a bug in GC_base that could result in a memory fault. +- A fix for a performance bug (and several other misfeatures) pointed + out by Dave Detlefs and Al Dosser. +- Use of dirty bit information for static data under Solaris 2.X. +- DEC Alpha/OSF1 support (thanks to Al Dosser). +- Incremental collection on more platforms. +- A more refined heap expansion policy. Less space usage by default. +- Various minor enhancements to reduce space usage, and to reduce + the amount of memory scanned by the collector. +- Uncollectable allocation without per object overhead. +- More conscientious handling of out-of-memory conditions. +- Fixed a bug in debugging stubborn allocation. +- Fixed a bug that resulted in occasional erroneous reporting of smashed + objects with debugging allocation. +- Fixed bogus leak reports of size 4096 blocks with FIND_LEAK. + + Version 3.2 fixed a serious and not entirely repeatable bug in + the incremental collector. It appeared only when dirty bit info + on the roots was available, which is normally only under Solaris. + It also added GC_general_register_disappearing_link, and some + testing code. Interface.c disappeared. + + Version 3.3 fixes several bugs and adds new ports: +- PCR-specific bugs. +- Missing locking in GC_free, redundant FASTUNLOCK + in GC_malloc_stubborn, and 2 bugs in + GC_unregister_disappearing_link. + All of the above were pointed out by Neil Sharman + (neil@cs.mu.oz.au). +- Common symbols allocated by the SunOS4.X dynamic loader + were not included in the root set. +- Bug in GC_finalize (reported by Brian Beuning and Al Dosser) +- Merged Amiga port from Jesper Peterson (untested) +- Merged NeXT port from Thomas Funke (significantly + modified and untested) + + Version 3.4: +- Fixed a performance bug in GC_realloc. +- Updated the amiga port. +- Added NetBSD and 386BSD ports. +- Added cord library. +- Added trivial performance enhancement for + ALL_INTERIOR_POINTERS. (Don't scan last word.) + + Version 3.5 +- Minor collections now mark from roots only once, if that + doesn't cause an excessive pause. +- The stack clearing heuristic was refined to prevent anomalies + with very heavily recursive programs and sparse stacks. +- Fixed a bug that prevented mark stack growth in some cases. + GC_objects_are_marked should be set to TRUE after a call + to GC_push_roots and as part of GC_push_marked, since + both can now set mark bits. I think this is only a performance + bug, but I wouldn't bet on it. It's certainly very hard to argue + that the old version was correct. +- Fixed an incremental collection bug that prevented it from + working at all when HBLKSIZE != getpagesize() +- Changed dynamic_loading.c to include gc_priv.h before testing + DYNAMIC_LOADING. SunOS dynamic library scanning + must have been broken in 3.4. +- Object size rounding now adapts to program behavior. +- Added a workaround (provided by Manuel Serrano and + colleagues) to a long-standing SunOS 4.X (and 3.X?) ld bug + that I had incorrectly assumed to have been squished. + The collector was broken if the text segment size was within + 32 bytes of a multiple of 8K bytes, and if the beginning of + the data segment contained interesting roots. The workaround + assumes a demand-loadable executable. The original may have + have "worked" in some other cases. +- Added dynamic library support under IRIX5. +- Added support for EMX under OS/2 (thanks to Ari Huttunen). + +Version 3.6: +- fixed a bug in the mark stack growth code that was introduced + in 3.4. +- fixed Makefile to work around DEC AXP compiler tail recursion + bug. + +Version 3.7: +- Added a workaround for an HP/UX compiler bug. +- Fixed another stack clearing performance bug. Reworked + that code once more. + +Version 4.0: +- Added support for Solaris threads (which was possible + only by reimplementing some fraction of Solaris threads, + since Sun doesn't currently make the thread debugging + interface available). +- Added non-threads win32 and win32S support. +- (Grudgingly, with suitable muttering of obscenities) renamed + files so that the collector distribution could live on a FAT + file system. Files that are guaranteed to be useless on + a PC still have long names. Gc_inline.h and gc_private.h + still exist, but now just include gc_inl.h and gc_priv.h. +- Fixed a really obscure bug in finalization that could cause + undetected mark stack overflows. (I would be surprised if + any real code ever tickled this one.) +- Changed finalization code to dynamically resize the hash + tables it maintains. (This probably does not matter for well- + -written code. It no doubt does for C++ code that overuses + destructors.) +- Added typed allocation primitives. Rewrote the marker to + accommodate them with more reasonable efficiency. This + change should also speed up marking for GC_malloc allocated + objects a little. See gc_typed.h for new primitives. +- Improved debugging facilities slightly. Allocation time + stack traces are now kept by default on SPARC/SUNOS4. + (Thanks to Scott Schwartz.) +- Added better support for small heap applications. +- Significantly extended cord package. Fixed a bug in the + implementation of lazily read files. Printf and friends now + have cord variants. Cord traversals are a bit faster. +- Made ALL_INTERIOR_POINTERS recognition the default. +- Fixed de so that it can run in constant space, independent + of file size. Added simple string searching to cords and de. +- Added the Hull-Ellis C++ interface. +- Added dynamic library support for OSF/1. + (Thanks to Al Dosser and Tim Bingham at DEC.) +- Changed argument to GC_expand_hp to be expressed + in units of bytes instead of heap blocks. (Necessary + since the heap block size now varies depending on + configuration. The old version was never very clean.) +- Added GC_get_heap_size(). The previous "equivalent" + was broken. +- Restructured the Makefile a bit. + +Since version 4.0: +- Changed finalization implementation to guarantee that + finalization procedures are called outside of the allocation + lock, making direct use of the interface a little less dangerous. + MAY BREAK EXISTING CLIENTS that assume finalizers + are protected by a lock. Since there seem to be few multithreaded + clients that use finalization, this is hopefully not much of + a problem. +- Fixed a gross bug in CORD_prev. +- Fixed a bug in blacklst.c that could result in unbounded + heap growth during startup on machines that do not clear + memory obtained from the OS (e.g. win32S). +- Ported de editor to win32/win32S. (This is now the only + version with a mouse-sensitive UI.) +- Added GC_malloc_ignore_off_page to allocate large arrays + in the presence of ALL_INTERIOR_POINTERS. +- Changed GC_call_with_alloc_lock to not disable signals in + the single-threaded case. +- Reduced retry count in GC_collect_or_expand for garbage + collecting when out of memory. +- Made uncollectable allocations bypass black-listing, as they + should. +- Fixed a bug in typed_test in test.c that could cause (legitimate) + GC crashes. +- Fixed some potential synchronization problems in finalize.c +- Fixed a real locking problem in typd_mlc.c. +- Worked around an AIX 3.2 compiler feature that results in + out of bounds memory references. +- Partially worked around an IRIX5.2 beta problem (which may + or may not persist to the final release). +- Fixed a bug in the heap integrity checking code that could + result in explicitly deallocated objects being identified as + smashed. Fixed a bug in the dbg_mlc stack saving code + that caused old argument pointers to be considered live. +- Fixed a bug in CORD_ncmp (and hence CORD_str). +- Repaired the OS2 port, which had suffered from bit rot + in 4.0. Worked around what appears to be CSet/2 V1.0 + optimizer bug. +- Fixed a Makefile bug for target "c++". + +Since version 4.1: +- Multiple bug fixes/workarounds in the Solaris threads version. + (It occasionally failed to locate some register contents for + marking. It also turns out that thr_suspend and friends are + unreliable in Solaris 2.3. Dirty bit reads appear + to be unreliable under some weird + circumstances. My stack marking code + contained a serious performance bug. The new code is + extremely defensive, and has not failed in several cpu + hours of testing. But no guarantees ...) +- Added MacOS support (thanks to Patrick Beard.) +- Fixed several syntactic bugs in gc_c++.h and friends. (These + didn't bother g++, but did bother most other compilers.) + Fixed gc_c++.h finalization interface. (It didn't.) +- 64 bit alignment for allocated objects was not guaranteed in a + few cases in which it should have been. +- Added GC_malloc_atomic_ignore_off_page. +- Added GC_collect_a_little. +- Added some prototypes to gc.h. +- Some other minor bug fixes (notably in Makefile). +- Fixed OS/2 / EMX port (thanks to Ari Huttunen). +- Fixed AmigaDOS port. (thanks to Michel Schinz). +- Fixed the DATASTART definition under Solaris. There + was a 1 in 16K chance of the collector missing the first + 64K of static data (and thus crashing). +- Fixed some blatant anachronisms in the README file. +- Fixed PCR-Makefile for upcoming PPCR release. + +Since version 4.2: +- Fixed SPARC alignment problem with GC_DEBUG. +- Fixed Solaris threads /proc workaround. The real + problem was an interaction with mprotect. +- Incorporated fix from Patrick Beard for gc_c++.h (now gc_cpp.h). +- Slightly improved allocator space utilization by + fixing the GC_size_map mechanism. +- Integrated some Sony News and MIPS RISCos 4.51 + patches. (Thanks to Nobuyuki Hikichi of + Software Research Associates, Inc. Japan) +- Fixed HP_PA alignment problem. (Thanks to + xjam@cork.cs.berkeley.edu.) +- Added GC_same_obj and friends. Changed GC_base + to return 0 for pointers past the end of large objects. + Improved GC_base performance with ALL_INTERIOR_POINTERS + on machines with a slow integer mod operation. + Added GC_PTR_ADD, GC_PTR_STORE, etc. to prepare + for preprocessor. +- changed the default on most UNIX machines to be that + signals are not disabled during critical GC operations. + This is still ANSI-conforming, though somewhat dangerous + in the presence of signal handlers. But the performance + cost of the alternative is sometimes problematic. + Can be changed back with a minor Makefile edit. +- renamed IS_STRING in gc.h, to CORD_IS_STRING, thus + following my own naming convention. Added the function + CORD_to_const_char_star. +- Fixed a gross bug in GC_finalize. Symptom: occasional + address faults in that function. (Thanks to Anselm + Baird-Smith (Anselm.BairdSmith@inria.fr) +- Added port to ICL DRS6000 running DRS/NX. Restructured + things a bit to factor out common code, and remove obsolete + code. Collector should now run under SUNOS5 with either + mprotect or /proc dirty bits. (Thanks to Douglas Steel + (doug@wg.icl.co.uk)). +- More bug fixes and workarounds for Solaris 2.X. (These were + mostly related to putting the collector in a dynamic library, + which didn't really work before. Also SOLARIS_THREADS + didn't interact well with dl_open.) Thanks to btlewis@eng.sun.com. +- Fixed a serious performance bug on the DEC Alpha. The text + segment was getting registered as part of the root set. + (Amazingly, the result was still fast enough that the bug + was not conspicuous.) The fix works on OSF/1, version 1.3. + Hopefully it also works on other versions of OSF/1 ... +- Fixed a bug in GC_clear_roots. +- Fixed a bug in GC_generic_malloc_words_small that broke + gc_inl.h. (Reported by Antoine de Maricourt. I broke it + in trying to tweak the Mac port.) +- Fixed some problems with cord/de under Linux. +- Fixed some cord problems, notably with CORD_riter4. +- Added DG/UX port. + Thanks to Ben A. Mesander (ben@piglet.cr.usgs.gov) +- Added finalization registration routines with weaker ordering + constraints. (This is necessary for C++ finalization with + multiple inheritance, since the compiler often adds self-cycles.) +- Filled the holes in the SCO port. (Thanks to Michael Arnoldus + <chime@proinf.dk>.) +- John Ellis' additions to the C++ support: From John: + +* I completely rewrote the documentation in the interface gc_c++.h +(later renamed gc_cpp.h). I've tried to make it both clearer and more +precise. + +* The definition of accessibility now ignores pointers from an +finalizable object (an object with a clean-up function) to itself. +This allows objects with virtual base classes to be finalizable by the +collector. Compilers typically implement virtual base classes using +pointers from an object to itself, which under the old definition of +accessibility prevented objects with virtual base classes from ever +being collected or finalized. + +* gc_cleanup now includes gc as a virtual base. This was enabled by +the change in the definition of accessibility. + +* I added support for operator new[]. Since most (all?) compilers +don't yet support operator new[], it is conditionalized on +-DOPERATOR_NEW_ARRAY. The code is untested, but its trivial and looks +correct. + +* The test program test_gc_c++ (later renamed test_cpp.cc) +tries to test for the C++-specific functionality not tested by the +other programs. +- Added <unistd.h> include to misc.c. (Needed for ppcr.) +- Added PowerMac port. (Thanks to Patrick Beard again.) +- Fixed "srcdir"-related Makefile problems. Changed things so + that all externally visible include files always appear in the + include subdirectory of the source. Made gc.h directly + includable from C++ code. (These were at Per + Bothner's suggestion.) +- Changed Intel code to also mark from ebp (Kevin Warne's + suggestion). +- Renamed C++ related files so they could live in a FAT + file system. (Charles Fiterman's suggestion.) +- Changed Windows NT Makefile to include C++ support in + gc.lib. Added C++ test as Makefile target. + +Since version 4.3: + - ASM_CLEAR_CODE was erroneously defined for HP + PA machines, resulting in a compile error. + - Fixed OS/2 Makefile to create a library. (Thanks to + Mark Boulter (mboulter@vnet.ibm.com)). + - Gc_cleanup objects didn't work if they were created on + the stack. Fixed. + - One copy of Gc_cpp.h in the distribution was out of + synch, and failed to document some known compiler + problems with explicit destructor invocation. Partially + fixed. There are probably other compilers on which + gc_cleanup is miscompiled. + - Fixed Makefile to pass C compiler flags to C++ compiler. + - Added Mac fixes. + - Fixed os_dep.c to work around what appears to be + a new and different VirtualQuery bug under newer + versions of win32S. + - GC_non_gc_bytes was not correctly maintained by + GC_free. Fixed. Thanks to James Clark (jjc@jclark.com). + - Added GC_set_max_heap_size. + - Changed allocation code to ignore blacklisting if it is preventing + use of a very large block of memory. This has the advantage + that naive code allocating very large objects is much more + likely to work. The downside is you might no + longer find out that such code should really use + GC_malloc_ignore_off_page. + - Changed GC_printf under win32 to close and reopen the file + between calls. FAT file systems otherwise make the log file + useless for debugging. + - Added GC_try_to_collect and GC_get_bytes_since_gc. These + allow starting an abortable collection during idle times. + This facility does not require special OS support. (Thanks to + Michael Spertus of Geodesic Systems for suggesting this. It was + actually an easy addition. Kumar Srikantan previously added a similar + facility to a now ancient version of the collector. At the time + this was much harder, and the result was less convincing.) + - Added some support for the Borland development environment. (Thanks + to John Ellis and Michael Spertus.) + - Removed a misfeature from checksums.c that caused unexpected + heap growth. (Thanks to Scott Schwartz.) + - Changed finalize.c to call WARN if it encounters a finalization cycle. + WARN is defined in gc_priv.h to write a message, usually to stdout. + In many environments, this may be inappropriate. + - Renamed NO_PARAMS in gc.h to GC_NO_PARAMS, thus adhering to my own + naming convention. + - Added GC_set_warn_proc to intercept warnings. + - Fixed Amiga port. (Thanks to Michel Schinz (schinz@alphanet.ch).) + - Fixed a bug in mark.c that could result in an access to unmapped + memory from GC_mark_from_mark_stack on machines with unaligned + pointers. + - Fixed a win32 specific performance bug that could result in scanning of + objects allocated with the system malloc. + - Added REDIRECT_MALLOC. + +Since version 4.4: + - Fixed many minor and one major README bugs. (Thanks to Franklin Chen + (chen@adi.com) for pointing out many of them.) + - Fixed ALPHA/OSF/1 dynamic library support. (Thanks to Jonathan Bachrach + (jonathan@harlequin.com)). + - Added incremental GC support (MPROTECT_VDB) for Linux (with some + help from Bruno Haible). + - Altered SPARC recognition tests in gc.h and config.h (mostly as + suggested by Fergus Henderson). + - Added basic incremental GC support for win32, as implemented by + Windows NT and Windows 95. GC_enable_incremental is a noop + under win32s, which doesn't implement enough of the VM interface. + - Added -DLARGE_CONFIG. + - Fixed GC_..._ignore_off_page to also function without + -DALL_INTERIOR_POINTERS. + - (Hopefully) fixed RS/6000 port. (Only the test was broken.) + - Fixed a performance bug in the nonincremental collector running + on machines supporting incremental collection with MPROTECT_VDB + (e.g. SunOS 4, DEC AXP). This turned into a correctness bug under + win32s with win32 incremental collection. (Not all memory protection + was disabled.) + - Fixed some ppcr related bit rot. + - Caused dynamic libraries to be unregistered before reregistering. + The old way turned out to be a performance bug on some machines. + - GC_root_size was not properly maintained under MSWIN32. + - Added -DNO_DEBUGGING and GC_dump. + - Fixed a couple of bugs arising with SOLARIS_THREADS + + REDIRECT_MALLOC. + - Added NetBSD/M68K port. (Thanks to Peter Seebach + <seebs@taniemarie.solon.com>.) + - Fixed a serious realloc bug. For certain object sizes, the collector + wouldn't scan the expanded part of the object. (Thanks to Clay Spence + (cds@peanut.sarnoff.com) for noticing the problem, and helping me to + track it down.) + +Since version 4.5: + - Added Linux ELF support. (Thanks to Arrigo Triulzi <arrigo@ic.ac.uk>.) + - GC_base crashed if it was called before any other GC_ routines. + This could happen if a gc_cleanup object was allocated outside the heap + before any heap allocation. + - The heap expansion heuristic was not stable if all objects had finalization + enabled. Fixed finalize.c to count memory in finalization queue and + avoid explicit deallocation. Changed alloc.c to also consider this count. + (This is still not recommended. It's expensive if nothing else.) Thanks + to John Ellis for pointing this out. + - GC_malloc_uncollectable(0) was broken. Thanks to Phong Vo for pointing + this out. + - The collector didn't compile under Linux 1.3.X. (Thanks to Fred Gilham for + pointing this out.) The current workaround is ugly, but expected to be + temporary. + - Fixed a formatting problem for SPARC stack traces. + - Fixed some '=='s in os_dep.c that should have been assignments. + Fortunately these were in code that should never be executed anyway. + (Thanks to Fergus Henderson.) + - Fixed the heap block allocator to only drop blacklisted blocks in small + chunks. Made BL_LIMIT self adjusting. (Both of these were in response + to heap growth observed by Paul Graham.) + - Fixed the Metrowerks/68K Mac code to also mark from a6. (Thanks + to Patrick Beard.) + - Significantly updated README.debugging. + - Fixed some problems with longjmps out of signal handlers, especially under + Solaris. Added a workaround for the fact that siglongjmp doesn't appear to + do the right thing with -lthread under Solaris. + - Added MSDOS/djgpp port. (Thanks to Mitch Harris (maharri@uiuc.edu).) + - Added "make reserved_namespace" and "make user_namespace". The + first renames ALL "GC_xxx" identifiers as "_GC_xxx". The second is the + inverse transformation. Note that doing this is guaranteed to break all + clients written for the other names. + - descriptor field for kind NORMAL in GC_obj_kinds with ADD_BYTE_AT_END + defined should be -ALIGNMENT not WORDS_TO_BYTES(-1). This is + a serious bug on machines with pointer alignment of less than a word. + - GC_ignore_self_finalize_mark_proc didn't handle pointers to very near the + end of the object correctly. Caused failures of the C++ test on a DEC Alpha + with g++. + - gc_inl.h still had problems. Partially fixed. Added warnings at the + beginning to hopefully specify the remaining dangers. + - Added DATAEND definition to config.h. + - Fixed some of the .h file organization. Fixed "make floppy". + +Since version 4.6: + - Fixed some compilation problems with -DCHECKSUMS (thanks to Ian Searle) + - Updated some Mac specific files to synchronize with Patrick Beard. + - Fixed a serious bug for machines with non-word-aligned pointers. + (Thanks to Patrick Beard for pointing out the problem. The collector + should fail almost any conceivable test immediately on such machines.) + +Since version 4.7: + - Changed a "comment" in a MacOS specific part of mach-dep.c that caused + gcc to fail on other platforms. + +Since version 4.8 + - More README.debugging fixes. + - Objects ready for finalization, but not finalized in the same GC + cycle, could be prematurely collected. This occasionally happened + in test_cpp. + - Too little memory was obtained from the system for very large + objects. That could cause a heap explosion if these objects were + not contiguous (e.g. under PCR), and too much of them was blacklisted. + - Due to an improper initialization, the collector was too hesitant to + allocate blacklisted objects immediately after system startup. + - Moved GC_arrays from the data into the bss segment by not explicitly + initializing it to zero. This significantly + reduces the size of executables, and probably avoids some disk accesses + on program startup. It's conceivable that it might break a port that I + didn't test. + - Fixed EMX_MAKEFILE to reflect the gc_c++.h to gc_cpp.h renaming which + occurred a while ago. + +Since 4.9: + - Fixed a typo around a call to GC_collect_or_expand in alloc.c. It broke + handling of out of memory. (Thanks to Patrick Beard for noticing.) + +Since 4.10: + - Rationalized (hopefully) GC_try_to_collect in an incremental collection + environment. It appeared to not handle a call while a collection was in + progress, and was otherwise too conservative. + - Merged GC_reclaim_or_delete_all into GC_reclaim_all to get rid of some + code. + - Added Patrick Beard's Mac fixes, with substantial completely untested + modifications. + - Fixed the MPROTECT_VDB code to deal with large pages and imprecise + fault addresses (as on an UltraSPARC running Solaris 2.5). Note that this + was not a problem in the default configuration, which uses PROC_VDB. + - The DEC Alpha assembly code needed to restore $gp between calls. + Thanks to Fergus Henderson for tracking this down and supplying a + patch. + - The write command for "de" was completely broken for large files. + I used the easiest portable fix, which involved changing the semantics + so that f.new is written instead of overwriting f. That's safer anyway. + - Added README.solaris2 with a discussion of the possible problems of + mixing the collector's sbrk allocation with malloc/realloc. + - Changed the data segment starting address for SGI machines. The + old code failed under IRIX6. + - Required double word alignment for MIPS. + - Various minor fixes to remove warnings. + - Attempted to fix some Solaris threads problems reported by Zhiying Chen. + In particular, the collector could try to fork a thread with the + world stopped as part of GC_thr_init. It also failed to deal with + the case in which the original thread terminated before the whole + process did. + - Added -DNO_EXECUTE_PERMISSION. This has a major performance impact + on the incremental collector under Irix, and perhaps under other + operating systems. + - Added some code to support allocating the heap with mmap. This may + be preferable under some circumstances. + - Integrated dynamic library support for HP. + (Thanks to Knut Tvedten <knuttv@ifi.uio.no>.) + - Integrated James Clark's win32 threads support, and made a number + of changes to it, many of which were suggested by Pontus Rydin. + This is still not 100% solid. + - Integrated Alistair Crooks' support for UTS4 running on an Amdahl + 370-class machine. + - Fixed a serious bug in explicitly typed allocation. Objects requiring + large descriptors where handled in a way that usually resulted in + a segmentation fault in the marker. (Thanks to Jeremy Fitzhardinge + for helping to track this down.) + - Added partial support for GNU win32 development. (Thanks to Fergus + Henderson.) + - Added optional support for Java-style finalization semantics. (Thanks + to Patrick Bridges.) This is recommended only for Java implementations. + - GC_malloc_uncollectable faulted instead of returning 0 when out of + memory. (Thanks to dan@math.uiuc.edu for noticing.) + - Calls to GC_base before the collector was initialized failed on a + DEC Alpha. (Thanks to Matthew Flatt.) + - Added base pointer checking to GC_REGISTER_FINALIZER in debugging + mode, at the suggestion of Jeremy Fitzhardinge. + - GC_debug_realloc failed for uncollectable objects. (Thanks to + Jeremy Fitzhardinge.) + - Explicitly typed allocation could crash if it ran out of memory. + (Thanks to Jeremy Fitzhardinge.) + - Added minimal support for a DEC Alpha running Linux. + - Fixed a problem with allocation of objects whose size overflowed + ptrdiff_t. (This now fails unconditionally, as it should.) + - Added the beginning of Irix pthread support. + - Integrated Xiaokun Zhu's fixes for djgpp 2.01. + - Added SGI-style STL allocator support (gc_alloc.h). + - Fixed a serious bug in README.solaris2. Multithreaded programs must include + gc.h with SOLARIS_THREADS defined. + - Changed GC_free so it actually deallocates uncollectable objects. + (Thanks to Peter Chubb for pointing out the problem.) + - Added Linux ELF support for dynamic libararies. (Thanks again to + Patrick Bridges.) + - Changed the Borland cc configuration so that the assembler is not + required. + - Fixed a bug in the C++ test that caused it to fail in 64-bit + environments. + +Since 4.11: + - Fixed ElfW definition in dyn_load.c. (Thanks to Fergus Henderson.) + This prevented the dynamic library support from compiling on some + older ELF Linux systems. + - Fixed UTS4 port (which I apparently mangled during the integration) + (Thanks to again to Alistair Crooks.) + - "Make C++" failed on Suns with SC4.0, due to a problem with "bool". + Fixed in gc_priv.h. + - Added more pieces for GNU win32. (Thanks to Timothy N. Newsham.) + The current state of things should suffice for at least some + applications. + - Changed the out of memory retry count handling as suggested by + Kenjiro Taura. (This matters only if GC_max_retries > 0, which + is no longer the default.) + - If a /proc read failed repeatedly, GC_written_pages was not updated + correctly. (Thanks to Peter Chubb for diagnosing this.) + - Under unlikely circumstances, the allocator could infinite loop in + an out of memory situation. (Thanks again to Kenjiro Taura for + identifying the problem and supplying a fix.) + - Fixed a syntactic error in the DJGPP code. (Thanks to Fergus + Henderson for finding this by inspection.) Also fixed a test program + problem with DJGPP (Thanks to Peter Monks.) + - Atomic uncollectable objects were not treated correctly by the + incremental collector. This resulted in weird log statistics and + occasional performance problems. (Thanks to Peter Chubb for pointing + this out.) + - Fixed some problems resulting from compilers that dont define + __STDC__. In this case void * and char * were used inconsistently + in some cases. (Void * should not have been used at all. If + you have an ANSI superset compiler that does not define __STDC__, + please compile with -D__STDC__=0. Thanks to Manuel Serrano and others + for pointing out the problem.) + - Fixed a compilation problem on Irix with -n32 and -DIRIX_THREADS. + Also fixed some other IRIX_THREADS problems which may or may not have + had observable symptoms. + - Fixed an HP PA compilation problem in dyn_load.c. (Thanks to + Philippe Queinnec.) + - SEGV fault handlers sometimes did not get reset correctly. (Thanks + to David Pickens.) + - Added a fix for SOLARIS_THREADS on Intel. (Thanks again to David + Pickens.) This probably needs more work to become functional. + - Fixed struct sigcontext_struct in os_dep.c for compilation under + Linux 2.1.X. (Thanks to Fergus Henderson.) + - Changed the DJGPP STACKBOTTOM and DATASTART values to those suggested + by Kristian Kristensen. These may still not be right, but it is + it is likely to work more often than what was there before. They may + even be exactly right. + - Added a #include <string.h> to test_cpp.cc. This appears to help + with HP/UX and gcc. (Thanks to assar@sics.se.) + - Version 4.11 failed to run in incremental mode on recent 64-bit Irix + kernels. This was a problem related to page unaligned heap segments. + Changed the code to page align heap sections on all platforms. + (I had mistakenly identified this as a kernel problem earlier. + It was not.) + - Version 4.11 did not make allocated storage executable, except on + one or two platforms, due to a bug in a #if test. (Thanks to Dave + Grove for pointing this out.) + - Added sparc_sunos4_mach_dep.s to support Sun's compilers under SunOS4. + - Added GC_exclude_static_roots. + - Fixed the object size mapping algorithm. This shouldn't matter, + but the old code was ugly. + - Heap checking code could die if one of the allocated objects was + larger than its base address. (Unsigned underflow problem. Thanks + to Clay Spence for isolating the problem.) + - Added RS6000 (AIX) dynamic library support and fixed STACK_BOTTOM. + (Thanks to Fred Stearns.) + - Added Fergus Henderson's patches for improved robustness with large + heaps and lots of blacklisting. + - Added Peter Chubb's changes to support Solaris Pthreads, to support + MMAP allocation in Solaris, to allow Solaris to find dynamic libraries + through /proc, to add malloc_typed_ignore_off_page, and a few other + minor features and bug fixes. + - The Solaris 2 port should not use sbrk. I received confirmation from + Sun that the use of sbrk and malloc in the same program is not + supported. The collector now defines USE_MMAP by default on Solaris. + - Replaced the djgpp makefile with Gary Leavens' version. + - Fixed MSWIN32 detection test. + - Added Fergus Henderson's patches to allow putting the collector into + a DLL under GNU win32. + - Added Ivan V. Demakov's port to Watcom C on X86. + - Added Ian Piumarta's Linux/PowerPC port. + - On Brian Burton's suggestion added PointerFreeGC to the placement + options in gc_cpp.h. This is of course unsafe, and may be controversial. + On the other hand, it seems to be needed often enough that it's worth + adding as a standard facility. + +Since 4.12: + - Fixed a crucial bug in the Watcom port. There was a redundant decl + of GC_push_one in gc_priv.h. + - Added FINALIZE_ON_DEMAND. + - Fixed some pre-ANSI cc problems in test.c. + - Removed getpagesize() use for Solaris. It seems to be missing in one + or two versions. + - Fixed bool handling for SPARCCompiler version 4.2. + - Fixed some files in include that had gotten unlinked from the main + copy. + - Some RS/6000 fixes (missing casts). Thanks to Toralf Foerster. + - Fixed several problems in GC_debug_realloc, affecting mostly the + FIND_LEAK case. + - GC_exclude_static_roots contained a buggy unsigned comparison to + terminate a loop. (Thanks to Wilson Ho.) + - CORD_str failed if the substring occurred at the last possible position. + (Only affects cord users.) + - Fixed Linux code to deal with RedHat 5.0 and integrated Peter Bigot's + os_dep.c code for dealing with various Linux versions. + - Added workaround for Irix pthreads sigaction bug and possible signal + misdirection problems. +Since alpha1: + - Changed RS6000 STACKBOTTOM. + - Integrated Patrick Beard's Mac changes. + - Alpha1 didn't compile on Irix m.n, m < 6. + - Replaced Makefile.dj with a new one from Gary Leavens. + - Added Andrew Stitcher's changes to support SCO OpenServer. + - Added PRINT_BLACK_LIST, to allow debugging of high densities of false + pointers. + - Added code to debug allocator to keep track of return address + in GC_malloc caller, thus giving a bit more context. + - Changed default behavior of large block allocator to more + aggressively avoid fragmentation. This is likely to slow down the + collector when it succeeds at reducing space cost. + - Integrated Fergus Henderson's CYGWIN32 changes. They are untested, + but needed for newer versions. + - USE_MMAP had some serious bugs. This caused the collector to fail + consistently on Solaris with -DSMALL_CONFIG. + - Added Linux threads support, thanks largely to Fergus Henderson. +Since alpha2: + - Fixed more Linux threads problems. + - Changed default GC_free_space_divisor to 3 with new large block allocation. + (Thanks to Matthew Flatt for some measurements that suggest the old + value sometimes favors space too much over time.) + - More CYGWIN32 fixes. + - Integrated Tyson-Dowd's Linux-M68K port. + - Minor HP PA and DEC UNIX fixes from Fergus Henderson. + - Integrated Christoffe Raffali's Linux-SPARC changes. + - Allowed for one more GC fixup iteration after a full GC in incremental + mode. Some quick measurements suggested that this significantly + reduces pause times even with smaller GC_RATE values. + - Moved some more GC data structures into GC_arrays. This decreases + pause times and GC overhead, but makes debugging slightly less convenient. + - Fixed namespace pollution problem ("excl_table"). + - Made GC_incremental a constant for -DSMALL_CONFIG, hopefully shrinking + that slightly. + - Added some win32 threads fixes. + - Integrated Ivan Demakov and David Stes' Watcom fixes. + - Various other minor fixes contributed by many people. + - Renamed config.h to gcconfig.h, since config.h tends to be used for + many other things. + - Integrated Matthew Flatt's support for 68K MacOS "far globals". + - Fixed up some of the dynamic library Makefile targets for consistency + across platforms. + - Fixed a USE_MMAP typo that caused out-of-memory handling to fail + on Solaris. + - Added code to test.c to test thread creation a bit more. + - Integrated GC_win32_free_heap, as suggested by Ivan Demakov. + - Fixed Solaris 2.7 stack base finding problem. (This may actually + have been done in an earlier alpha release.) +Since alpha3: + - Fixed MSWIN32 recognition test, which interfered with cygwin. + - Removed unnecessary gc_watcom.asm from distribution. Removed + some obsolete README.win32 text. + - Added Alpha Linux incremental GC support. (Thanks to Philipp Tomsich + for code for retrieving the fault address in a signal handler.) + Changed Linux signal handler context argument to be a pointer. + - Took care of some new warnings generated by the 7.3 SGI compiler. + - Integrated Phillip Musumeci's FreeBSD/ELF fixes. + - -DIRIX_THREADS was broken with the -o32 ABI (typo in gc_priv.h> + +Since 4.13: + - Fixed GC_print_source_ptr to not use a prototype. + - generalized CYGWIN test. + - gc::new did the wrong thing with PointerFreeGC placement. + (Thanks to Rauli Ruohonen.) + - In the ALL_INTERIOR_POINTERS (default) case, some callee-save register + values could fail to be scanned if the register was saved and + reused in a GC frame. This showed up in verbose mode with gctest + compiled with an unreleased SGI compiler. I vaguely recall an old + bug report that may have been related. The bug was probably quite old. + (The problem was that the stack scanning could be deferred until + after the relevant frame was overwritten, and the new save location + might be outside the scanned area. Fixed by more eager stack scanning.) + - PRINT_BLACK_LIST had some problems. A few source addresses were garbage. + - Replaced Makefile.dj and added -I flags to cord make targets. + (Thanks to Gary Leavens.) + - GC_try_to_collect was broken with the nonincremental collector. + - gc_cleanup destructors could pass the wrong address to + GC_register_finalizer_ignore_self in the presence of multiple + inheritance. (Thanks to Darrell Schiebel.) + - Changed PowerPC Linux stack finding code. + +Since 4.14alpha1 + - -DSMALL_CONFIG did not work reliably with large (> 4K) pages. + Recycling the mark stack during expansion could result in a size + zero heap segment, which confused things. (This was probably also an + issue with the normal config and huge pages.) + - Did more work to make sure that callee-save registers were scanned + completely, even with the setjmp-based code. Added USE_GENERIC_PUSH_REGS + macro to facilitate testing on machines I have access to. + - Added code to explicitly push register contents for win32 threads. + This seems to be necessary. (Thanks to Pierre de Rop.) + +Since 4.14alpha2 + - changed STACKBOTTOM for DJGPP (Thanks to Salvador Eduardo Tropea). + +Since 4.14 + - Reworked large block allocator. Now uses multiple doubly linked free + lists to approximate best fit. + - Changed heap expansion heuristic. Entirely free blocks are no longer + counted towards the heap size. This seems to have a major impact on + heap size stability; the old version could expand the heap way too + much in the presence of large block fragmentation. + - added -DGC_ASSERTIONS and some simple assertions inside the collector. + This is mainlyt for collector debugging. + - added -DUSE_MUNMAP to allow the heap to shrink. Suupported on only + a few UNIX-like platforms for now. + - added GC_dump_regions() for debugging of fragmentation issues. + - Changed PowerPC pointer alignment under Linux to 4. (This needs + checking by someone who has one. The suggestions came to me via a + rather circuitous path.) + - Changed the Linux/Alpha port to walk the data segment backwards until + it encounters a SIGSEGV. The old way to find the start of the data + segment broke with a recent release. + - cordxtra.c needed to call GC_REGISTER_FINALIZER instead of + GC_register_finalizer, so that it would continue to work with GC_DEBUG. + - allochblk sometimes cleared the wrong block for debugging purposes + when it dropped blacklisted blocks. This could result in spurious + error reports with GC_DEBUG. + - added MACOS X Server support. (Thanks to Andrew Stone.) + - Changed the Solaris threads code to ignore stack limits > 8 MB with + a warning. Empirically, it is not safe to access arbitrary pages + in such large stacks. And the dirty bit implementation does not + guarantee that none of them will be accessed. + - Integrated Martin Tauchmann's Amiga changes. + - Integrated James Dominy's OpenBSD/SPARC port. + +Since 5.0alpha1 + - Fixed bugs introduced in alpha1 (OpenBSD & large block initialization). + - Added -DKEEP_BACK_PTRS and backptr.h interface. (The implementation + idea came from Al Demers.) + +Since 5.0alpha2 + - Added some highly incomplete code to support a copied young generation. + Comments on nursery.h are appreciated. + - Changed -DFIND_LEAK, -DJAVA_FINALIZATION, and -DFINALIZE_ON_DEMAND, + so the same effect could be obtained with a runtime switch. This is + a step towards standardizing on a single dynamic GC library. + - Significantly changed the way leak detection is handled, as a consequence + of the above. + +To do: + - Very large root set sizes (> 16 MB or so) could cause the collector + to abort with an unexpected mark stack overflow. (Thanks again to + Peter Chubb.) NOT YET FIXED. Workaround is to increase the initial + size. + - The SGI version of the collector marks from mmapped pages, even + if they are not part of dynamic library static data areas. This + causes performance problems with some SGI libraries that use mmap + as a bitmap allocator. NOT YET FIXED. It may be possible to turn + off DYNAMIC_LOADING in the collector as a workaround. It may also + be possible to conditionally intercept mmap and use GC_exclude_static_roots. + The real fix is to walk rld data structures, which looks possible. + - Integrate MIT and DEC pthreads ports. + - Deal with very uneven black-listing distributions. If all the black listed + blocks reside in the newly allocated heap section, the heuristic for + temporarily ignoring black-listing fails, and the heap grows too much. + (This was observed in only one case, and could be worked around, but ...) + - Some platform specific updates are waiting for 4.15alpha1. diff --git a/gc/README.Mac b/gc/README.Mac new file mode 100644 index 0000000..04f4682 --- /dev/null +++ b/gc/README.Mac @@ -0,0 +1,385 @@ +Patrick Beard's Notes for building GC v4.12 with CodeWarrior Pro 2: +---------------------------------------------------------------------------- +The current build environment for the collector is CodeWarrior Pro 2. +Projects for CodeWarrior Pro 2 (and for quite a few older versions) +are distributed in the file Mac_projects.sit.hqx. The project file +:Mac_projects:gc.prj builds static library versions of the collector. +:Mac_projects:gctest.prj builds the GC test suite. + +Configuring the collector is still done by editing the files +:Mac_files:MacOS_config.h and :Mac_files:MacOS_Test_config.h. + +Lars Farm's suggestions on building the collector: +---------------------------------------------------------------------------- +Garbage Collection on MacOS - a manual 'MakeFile' +------------------------------------------------- + +Project files and IDE's are great on the Macintosh, but they do have +problems when used as distribution media. This note tries to provide +porting instructions in pure TEXT form to avoid those problems. A manual +'makefile' if you like. + + GC version: 4.12a2 + Codewarrior: CWPro1 + date: 18 July 1997 + +The notes may or may not apply to earlier or later versions of the +GC/CWPro. Actually, they do apply to earlier versions of both except that +until recently a project could only build one target so each target was a +separate project. The notes will most likely apply to future versions too. +Possibly with minor tweaks. + +This is just to record my experiences. These notes do not mean I now +provide a supported port of the GC to MacOS. It works for me. If it works +for you, great. If it doesn't, sorry, try again...;-) Still, if you find +errors, please let me know. + + mailto: lars.farm@ite.mh.se + + address: Lars Farm + Kr�nv�gen 33b + 856 44 Sundsvall + Sweden + +Porting to MacOS is a bit more complex than it first seems. Which MacOS? +68K/PowerPC? Which compiler? Each supports both 68K and PowerPC and offer a +large number of (unique to each environment) compiler settings. Each +combination of compiler/68K/PPC/settings require a unique combination of +standard libraries. And the IDE's does not select them for you. They don't +even check that the library is built with compatible setting and this is +the major source of problems when porting the GC (and otherwise too). + +You will have to make choices when you configure the GC. I've made some +choices here, but there are other combinations of settings and #defines +that work too. + +As for target settings the major obstacles may be: +- 68K Processor: check "4-byte Ints". +- PPC Processor: uncheck "Store Static Data in TOC". + +What you need to do: +=================== + +1) Build the GC as a library +2) Test that the library works with 'test.c'. +3) Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc'. + +1) The Libraries: +================= +I made one project with four targets (68K/PPC tempmem or appheap). One target +will suffice if you're able to decide which one you want. I wasn't... + +Codewarrior allows a large number of compiler/linker settings. I used these: + +Settings shared by all targets: +------------------------------ +o Access Paths: + - User Paths: the GC folder + - System Paths: {Compiler}:Metrowerks Standard Library: + {Compiler}:MacOS Support:Headers: + {Compiler}:MacOS Support:MacHeaders: +o C/C++ language: + - inlining: normal + - direct to SOM: off + - enable/check: exceptions, RTTI, bool (and if you like pool strings) + +PowerPC target settings +----------------------- +o Target Settings: + - name of target + - MacOS PPC Linker +o PPC Target + - name of library +o C/C++ language + - prefix file as described below +o PPC Processor + - Struct Alignment: PowerPC + - uncheck "Store Static Data in TOC" -- important! + I don't think the others matter, I use full optimization and its ok +o PPC Linker + - Factory Settings (SYM file with full paths, faster linking, dead-strip + static init, Main: __start) + + +68K target settings +------------------- +o Target Settings: + - name of target + - MacOS 68K Linker +o 68K Target + - name of library + - A5 relative data +o C/C++ language + - prefix file as described below +o 68K Processor + - Code model: smart + - Struct alignment: 68K + - FP: SANE + - enable 4-Byte Ints -- important! + I don't think the others matter. I selected... + - enable: 68020 + - enable: global register allocation +o IR Optimizer + - enable: Optimize Space, Optimize Speed + I suppose the others would work too, but haven't tried... +o 68K Linker + - Factory Settings (New Style MacsBug,SYM file with full paths, + A6 Frames, fast link, Merge compiler glue into segment 1, + dead-strip static init) + +Prefix Files to configure the GC sources +---------------------------------------- +The Codewarrior equivalent of commandline compilers -DNAME=X is to use +prefix-files. A TEXT file that is automatically #included before the first byte +of every source file. I used these: + +---- ( cut here ) ---- gc_prefix_tempmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY + #define USE_TEMPORARY_MEMORY +---- ( cut here ) ---- gc_prefix_appmem.h -- 68K and PPC ----- + #include "gc_prefix_common.h" + #undef USE_TEMPORARY_MEMORY +// #define USE_TEMPORARY_MEMORY + +---- ( cut here ) ---- gc_prefix_common.h -------------------- +// gc_prefix_common.h +// ------------------ +// Codewarrior prefix file to configure the GC libraries +// +// prefix files are the Codewarrior equivalent of the +// command line option -Dname=x frequently seen in makefiles + +#if !__MWERKS__ + #error only tried this with Codewarrior +#endif + +#if macintosh + #define MSL_USE_PRECOMPILED_HEADERS 0 + #include <ansi_prefix.mac.h> + #ifndef __STDC__ + #define __STDC__ 0 + #endif + + // See list of #defines to configure the library in: 'MakeFile' + // see also README + + #define SILENT // no collection messages. In case + // of trouble you might want this off + #define ALL_INTERIOR_POINTERS // follows interior pointers. +//#define DONT_ADD_BYTE_AT_END // disables the padding if defined. +//#define SMALL_CONFIG // whether to use a smaller heap. + #define NO_SIGNALS // signals aren't real on the Macintosh. + #define ATOMIC_UNCOLLECTABLE // GC_malloc_atomic_uncollectable() + + // define either or none as per personal preference + // used in malloc.c + #define REDIRECT_MALLOC GC_malloc +//#define REDIRECT_MALLOC GC_malloc_uncollectable + // if REDIRECT_MALLOC is #defined make sure that the GC library + // is listed before the ANSI/ISO libs in the Codewarrior + // 'Link order' panel +//#define IGNORE_FREE + + // mac specific configs +//#define USE_TEMPORARY_MEMORY // use Macintosh temporary memory. +//#define SHARED_LIBRARY_BUILD // build for use in a shared library. + +#else + // could build Win32 here too, or in the future + // Rhapsody PPC-mach, Rhapsody PPC-MacOS, + // Rhapsody Intel-mach, Rhapsody Intel-Win32,... + // ... ugh this will get messy ... +#endif + +// make sure ints are at least 32-bit +// ( could be set to 16-bit by compiler settings (68K) ) + +struct gc_private_assert_intsize_{ char x[ sizeof(int)>=4 ? 1 : 0 ]; }; + +#if __powerc + #if __option(toc_data) + #error turn off "store static data in TOC" when using GC + // ... or find a way to add TOC to the root set...(?) + #endif +#endif +---- ( cut here ) ---- end of gc_prefix_common.h ----------------- + +Files to build the GC libraries: +-------------------------------- + allchblk.c + alloc.c + blacklst.c + checksums.c + dbg_mlc.c + finalize.c + headers.c + mach_dep.c + MacOS.c -- contains MacOS code + malloc.c + mallocx.c + mark.c + mark_rts.c + misc.c + new_hblk.c + obj_map.c + os_dep.c -- contains MacOS code + ptr_chck.c + reclaim.c + stubborn.c + typd_mlc.c + gc++.cc -- this is 'gc_cpp.cc' with less 'inline' and + -- throw std::bad_alloc when out of memory + -- gc_cpp.cc works just fine too + +2) Test that the library works with 'test.c'. +============================================= + +The test app is just an ordinary ANSI-C console app. Make sure settings +match the library you're testing. + +Files +----- + test.c + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +---- ( cut here ) ---- gc_prefix_testlib.h -- all libs ----- +#define MSL_USE_PRECOMPILED_HEADERS 0 +#include <ansi_prefix.mac.h> +#undef NDEBUG + +#define ALL_INTERIOR_POINTERS /* for GC_priv.h */ +---- ( cut here ) ---- + +3) Test that the C++ interface 'gc_cpp.cc/h' works with 'test_cpp.cc'. + +The test app is just an ordinary ANSI-C console app. Make sure settings match +the library you're testing. + +Files +----- + test_cpp.cc + the GC library to test -- link order before ANSI libs + suitable Mac+ANSI libraries + +prefix: +------ +same as for test.c + +For convenience I used one test-project with several targets so that all +test apps are build at once. Two for each library to test: test.c and +gc_app.cc. When I was satisfied that the libraries were ok. I put the +libraries + gc.h + the c++ interface-file in a folder that I then put into +the MSL hierarchy so that I don't have to alter access-paths in projects +that use the GC. + +After that, just add the proper GC library to your project and the GC is in +action! malloc will call GC_malloc and free GC_free, new/delete too. You +don't have to call free or delete. You may have to be a bit cautious about +delete if you're freeing other resources than RAM. See gc_cpp.h. You can +also keep coding as always with delete/free. That works too. If you want, +"include <gc.h> and tweak it's use a bit. + +Symantec SPM +============ +It has been a while since I tried the GC in SPM, but I think that the above +instructions should be sufficient to guide you through in SPM too. SPM +needs to know where the global data is. Use the files 'datastart.c' and +'dataend.c'. Put 'datastart.c' at the top of your project and 'dataend.c' +at the bottom of your project so that all data is surrounded. This is not +needed in Codewarrior because it provides intrinsic variables +__datastart__, __data_end__ that wraps all globals. + +Source Changes (GC 4.12a2) +========================== +Very few. Just one tiny in the GC, not strictly needed. +- MacOS.c line 131 in routine GC_MacFreeTemporaryMemory() + change # if !defined(SHARED_LIBRARY_BUILD) + to # if !defined(SILENT) && !defined(SHARED_LIBRARY_BUILD) + To turn off a message when the application quits (actually, I faked + this change by #defining SHARED_LIBRARY_BUILD in a statically linked + library for more than a year without ill effects but perhaps this is + better). + +- test_cpp.cc + made the first lines of main() look like this: + ------------ + int main( int argc, char* argv[] ) { + #endif + #if macintosh // MacOS + char* argv_[] = {"test_cpp","10"}; // doesn't + argv=argv_; // have a + argc = sizeof(argv_)/sizeof(argv_[0]); // commandline + #endif // + + int i, iters, n; + # ifndef __GNUC__ + alloc dummy_to_fool_the_compiler_into_doing_things_it_currently_cant_handle; + ------------ + +- config.h [now gcconfig.h] + __MWERKS__ does not have to mean MACOS. You can use Codewarrior to + build a Win32 or BeOS library and soon a Rhapsody library. You may + have to change that #if... + + + + It worked for me, hope it works for you. + + Lars Farm + 18 July 1997 +---------------------------------------------------------------------------- + + +Patrick Beard's instructions (may be dated): + +v4.3 of the collector now runs under Symantec C++/THINK C v7.0.4, and +Metrowerks C/C++ v4.5 both 68K and PowerPC. Project files are provided +to build and test the collector under both development systems. + +Configuration +------------- + +To configure the collector, under both development systems, a prefix file +is used to set preprocessor directives. This file is called "MacOS_config.h". +Also to test the collector, "MacOS_Test_config.h" is provided. + +Testing +------- + +To test the collector (always a good idea), build one of the gctest projects, +gctest.� (Symantec C++/THINK C), mw/gctest.68K.�, or mw/gctest.PPC.�. The +test will ask you how many times to run; 1 should be sufficient. + +Building +-------- + +For your convenience project files for the major Macintosh development +systems are provided. + +For Symantec C++/THINK C, you must build the two projects gclib-1.� and +gclib-2.�. It has to be split up because the collector has more than 32k +of static data and no library can have more than this in the Symantec +environment. (Future versions will probably fix this.) + +For Metrowerks C/C++ 4.5 you build gc.68K.�/gc.PPC.� and the result will +be a library called gc.68K.lib/gc.PPC.lib. + +Using +----- + +Under Symantec C++/THINK C, you can just add the gclib-1.� and gclib-2.� +projects to your own project. Under Metrowerks, you add gc.68K.lib or +gc.PPC.lib and two additional files. You add the files called datastart.c +and dataend.c to your project, bracketing all files that use the collector. +See mw/gctest.� for an example. + +Include the projects/libraries you built above into your own project, +#include "gc.h", and call GC_malloc. You don't have to call GC_free. + + +Patrick C. Beard +January 4, 1995 diff --git a/gc/README.OS2 b/gc/README.OS2 new file mode 100644 index 0000000..5345bbd --- /dev/null +++ b/gc/README.OS2 @@ -0,0 +1,6 @@ +The code assumes static linking, and a single thread. The editor de has +not been ported. The cord test program has. The supplied OS2_MAKEFILE +assumes the IBM C Set/2 environment, but the code shouldn't. + +Since we haven't figured out hoe to do perform partial links or to build static +libraries, clients currently need to link against a long list of executables. diff --git a/gc/README.QUICK b/gc/README.QUICK new file mode 100644 index 0000000..3273c8b --- /dev/null +++ b/gc/README.QUICK @@ -0,0 +1,41 @@ +Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers +Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + + +For more details and the names of other contributors, see the +README file and gc.h. This file describes typical use of +the collector on a machine that is already supported. + +INSTALLATION: +Under UN*X, type "make test". Under OS/2 or Windows NT, copy the +appropriate makefile to MAKEFILE, read it, and type "nmake test". +Read the machine specific README if one exists. The only way to +develop code with the collector for Windows 3.1 is to develop under +Windows NT, and then to use win32S. + +If you wish to use the cord (structured string) library type +"make cords". (This requires an ANSI C compiler. You may need +to redefine CC in the Makefile.) + +If you wish to use the collector from C++, type +"make c++". These add further files to gc.a and to the include +subdirectory. See cord/cord.h and gc_c++.h. + +TYPICAL USE: +Include "gc.h" from this directory. Link against the appropriate library +("gc.a" under UN*X). Replace calls to malloc by calls to GC_MALLOC, +and calls to realloc by calls to GC_REALLOC. If the object is known +to never contain pointers, use GC_MALLOC_ATOMIC instead of +GC_MALLOC. + +Define GC_DEBUG before including gc.h for additional checking. + diff --git a/gc/README.alpha b/gc/README.alpha new file mode 100644 index 0000000..213a13e --- /dev/null +++ b/gc/README.alpha @@ -0,0 +1,22 @@ +Should work under OSF/1 and Linux. Currently no VMS or NT support, though +the latter shouldn't be hard. + +Incremental gc not yet supported under Linux because signal handler +for SIGSEGV can't get a hold of fault address. Dynamic library support +is also missing from Linux/alpha, probably for no good reason. + +Currently there is no thread support in the standard distribution. There +exists a separate port to DEC Unix pthreads. It should be possible to +port the X86 Linux threads support to Alpha without much trouble. + +If you get asssembler errors, be sure to read the first few lines of the +Makefile. + +From Philippe Queinnec: + +System: DEC/Alpha OSF1 v3.2, vendor cc +Problem: can't link if libgc has been compiled with "cc -std1". + It works if the library has been compiled with either gcc or "cc" + alone. The problem is because the variable "end" is not defined if + compiling in std1 mode (see man ld). +Proposed fix: none. Don't use cc -std1 ! diff --git a/gc/README.amiga b/gc/README.amiga new file mode 100644 index 0000000..47b1588 --- /dev/null +++ b/gc/README.amiga @@ -0,0 +1,180 @@ +=========================================================================== + Martin Tauchmann's notes (1-Apr-99) +=========================================================================== + +Works now, also with the GNU-C compiler V2.7.2.1. <ftp://ftp.unina.it/pub/amiga/geekgadgets/amiga/m68k/snapshots/971125/amiga-bin/> +Modify the `Makefile` +CC=cc $(ABI_FLAG) +to +CC=gcc $(ABI_FLAG) + +TECHNICAL NOTES + +- `GC_get_stack_base()`, `GC_register_data_segments()` works now with every + C compiler; also Workbench. + +- Removed AMIGA_SKIP_SEG, but the Code-Segment must not be scanned by GC. + + +PROBLEMS +- When the Linker, does`t merge all Code-Segments to an single one. LD of GCC + do it always. + +- With ixemul.library V47.3, when an GC program launched from another program + (example: `Make` or `if_mach M68K AMIGA gctest`), `GC_register_data_segments()` + found the Segment-List of the caller program. + Can be fixed, if the run-time initialization code (for C programs, usually *crt0*) + support `__data` and `__bss`. + +- PowerPC Amiga currently not supported. + +- Dynamic libraries (dyn_load.c) not supported. + + +TESTED WITH SOFTWARE + +`Optimized Oberon 2 C` (oo2c) <http://cognac.informatik.uni-kl.de/download/index.html> + + +TESTED WITH HARDWARE + +MC68030 + + +CONTACT + +Please, contact me at <martintauchmann@bigfoot.com>, when you change the +Amiga port. <http://martintauchmann.home.pages.de> + +=========================================================================== + Michel Schinz's notes +=========================================================================== +WHO DID WHAT + +The original Amiga port was made by Jesper Peterson. I (Michel Schinz) +modified it slightly to reflect the changes made in the new official +distributions, and to take advantage of the new SAS/C 6.x features. I also +created a makefile to compile the "cord" package (see the cord +subdirectory). + +TECHNICAL NOTES + +In addition to Jesper's notes, I have the following to say: + +- Starting with version 4.3, gctest checks to see if the code segment is + added to the root set or not, and complains if it is. Previous versions + of this Amiga port added the code segment to the root set, so I tried to + fix that. The only problem is that, as far as I know, it is impossible to + know which segments are code segments and which are data segments (there + are indeed solutions to this problem, like scanning the program on disk + or patch the LoadSeg functions, but they are rather complicated). The + solution I have chosen (see os_dep.c) is to test whether the program + counter is in the segment we are about to add to the root set, and if it + is, to skip the segment. The problems are that this solution is rather + awkward and that it works only for one code segment. This means that if + your program has more than one code segment, all of them but one will be + added to the root set. This isn't a big problem in fact, since the + collector will continue to work correctly, but it may be slower. + + Anyway, the code which decides whether to skip a segment or not can be + removed simply by not defining AMIGA_SKIP_SEG. But notice that if you do + so, gctest will complain (it will say that "GC_is_visible produced wrong + failure indication"). However, it may be useful if you happen to have + pointers stored in a code segment (you really shouldn't). + + If anyone has a good solution to the problem of finding, when a program + is loaded in memory, whether a segment is a code or a data segment, + please let me know. + +PROBLEMS + +If you have any problem with this version, please contact me at +schinz@alphanet.ch (but do *not* send long files, since we pay for +every mail!). + +=========================================================================== + Jesper Peterson's notes +=========================================================================== + +ADDITIONAL NOTES FOR AMIGA PORT + +These notes assume some familiarity with Amiga internals. + +WHY I PORTED TO THE AMIGA + +The sole reason why I made this port was as a first step in getting +the Sather(*) language on the Amiga. A port of this language will +be done as soon as the Sather 1.0 sources are made available to me. +Given this motivation, the garbage collection (GC) port is rather +minimal. + +(*) For information on Sather read the comp.lang.sather newsgroup. + +LIMITATIONS + +This port assumes that the startup code linked with target programs +is that supplied with SAS/C versions 6.0 or later. This allows +assumptions to be made about where to find the stack base pointer +and data segments when programs are run from WorkBench, as opposed +to running from the CLI. The compiler dependent code is all in the +GC_get_stack_base() and GC_register_data_segments() functions, but +may spread as I add Amiga specific features. + +Given that SAS/C was assumed, the port is set up to be built with +"smake" using the "SMakefile". Compiler options in "SCoptions" can +be set with "scopts" program. Both "smake" and "scopts" are part of +the SAS/C commercial development system. + +In keeping with the porting philosophy outlined above, this port +will not behave well with Amiga specific code. Especially not inter- +process comms via messages, and setting up public structures like +Intuition objects or anything else in the system lists. For the +time being the use of this library is limited to single threaded +ANSI/POSIX compliant or near-complient code. (ie. Stick to stdio +for now). Given this limitation there is currently no mechanism for +allocating "CHIP" or "PUBLIC" memory under the garbage collector. +I'll add this after giving it considerable thought. The major +problem is the entire physical address space may have to me scanned, +since there is no telling who we may have passed memory to. + +If you allocate your own stack in client code, you will have to +assign the pointer plus stack size to GC_stackbottom. + +The initial stack size of the target program can be compiled in by +setting the __stack symbol (see SAS documentaion). It can be over- +ridden from the CLI by running the AmigaDOS "stack" program, or from +the WorkBench by setting the stack size in the tool types window. + +SAS/C COMPILER OPTIONS (SCoptions) + +You may wish to check the "CPU" code option is appropriate for your +intended target system. + +Under no circumstances set the "StackExtend" code option in either +compiling the library or *ANY* client code. + +All benign compiler warnings have been suppressed. These mainly +involve lack of prototypes in the code, and dead assignments +detected by the optimizer. + +THE GOOD NEWS + +The library as it stands is compatible with the GigaMem commercial +virtual memory software, and probably similar PD software. + +The performance of "gctest" on an Amiga 2630 (68030 @ 25Mhz) +compares favourably with an HP9000 with similar architecture (a 325 +with a 68030 I think). + +----------------------------------------------------------------------- + +The Amiga port has been brought to you by: + +Jesper Peterson. + +jep@mtiame.mtia.oz.au (preferred, but 1 week turnaround) +jep@orca1.vic.design.telecom.au (that's orca<one>, 1 day turnaround) + +At least one of these addresses should be around for a while, even +though I don't work for either of the companies involved. + diff --git a/gc/README.debugging b/gc/README.debugging new file mode 100644 index 0000000..80635c2 --- /dev/null +++ b/gc/README.debugging @@ -0,0 +1,58 @@ +Debugging suggestions: + +****If you get a segmentation fault or bus error while debugging with a debugger: +If the fault occurred in GC_find_limit, or with incremental collection enabled, this is probably normal. The collector installs handlers to take care of these. You will not see these unless you are using a debugger. Your debugger should allow you to continue. It's preferable to tell the debugger to ignore SIGBUS and SIGSEGV ("handle" in gdb, "ignore" in most versions of dbx) and set a breakpoint in abort. The collector will call abort if the signal had another cause, and there was not other handler previously installed. I recommend debugging without incremental collection if possible. (This applies directly to UNIX systems. Debugging with incremental collection under win32 is worse. See README.win32.) + +****If you get warning messages informing you that the collector needed to allocate blacklisted blocks: + +0) Ignore these warnings while you are using GC_DEBUG. Some of the routines mentioned below don't have debugging equivalents. (Alternatively, write the missing routines and send them to me.) + +1) Replace allocator calls that request large blocks with calls to GC_malloc_ignore_off_page or GC_malloc_atomic_ignore_off_page. You may want to set a breakpoint in GC_default_warn_proc to help you identify such calls. Make sure that a pointer to somewhere near the beginning of the resulting block is maintained in a (preferably volatile) variable as long as the block is needed. + +2) If the large blocks are allocated with realloc, I suggest instead allocating them with something like the following. Note that the realloc size increment should be fairly large (e.g. a factor of 3/2) for this to exhibit reasonable performance. But we all know we should do that anyway. + +void * big_realloc(void *p, size_t new_size) +{ + size_t old_size = GC_size(p); + void * result; + + if (new_size <= 10000) return(GC_realloc(p, new_size)); + if (new_size <= old_size) return(p); + result = GC_malloc_ignore_off_page(new_size); + if (result == 0) return(0); + memcpy(result,p,old_size); + GC_free(p); + return(result); +} + +3) In the unlikely case that even relatively small object (<20KB) allocations are triggering these warnings, then your address space contains lots of "bogus pointers", i.e. values that appear to be pointers but aren't. Usually this can be solved by using GC_malloc_atomic or the routines in gc_typed.h to allocate large pointerfree regions of bitmaps, etc. Sometimes the problem can be solved with trivial changes of encoding in certain values. It is possible, though not pleasant, to identify the source of the bogus pointers by setting a breakpoint in GC_add_to_black_list_stack, and looking at the value of current_p in the GC_mark_from_mark_stack frame. Current_p contains the address of the bogus pointer. + +4) If you get only a fixed number of these warnings, you are probably only introducing a bounded leak by ignoring them. If the data structures being allocated are intended to be permanent, then it is also safe to ignore them. The warnings can be turned off by calling GC_set_warn_proc with a procedure that ignores these warnings (e.g. by doing absolutely nothing). + + +****If the collector dies in GC_malloc while trying to remove a free list element: + +1) With > 99% probability, you wrote past the end of an allocated object. Try setting GC_DEBUG and using the debugging facilities in gc.h. + + +****If the heap grows too much: + +1) Consider using GC_malloc_atomic for objects containing nonpointers. This is especially important for large arrays containg compressed data, pseudo-random numbers, and the like. (This isn't all that likely to solve your problem, but it's a useful and easy optimization anyway, and this is a good time to try it.) If you allocate large objects containg only one or two pointers at the beginning, either try the typed allocation primitives is gc.h, or separate out the pointerfree component. +2) If you are using the collector in its default mode, with interior pointer recognition enabled, consider using GC_malloc_ignore_off_page to allocate large objects. (See gc.h and above for details. Large means > 100K in most environments.) +3) GC_print_block_list() will print a list of all currently allocated heap blocks and what size objects they contain. GC_print_hblkfreelist() will print a list of free heap blocks, and whether they are blacklisted. GC_dump calls both of these, and also prints information about heap sections, and root segments. +4) Write a tool that traces back references to the appropriate root. Send me the code. (I have code that does this for old PCR.) + + +****If the collector appears to be losing objects: + +1) Replace all calls to GC_malloc_atomic and typed allocation by GC_malloc calls. If this fixes the problem, gradually reinsert your optimizations. +2) You may also want to try the safe(r) pointer manipulation primitives in gc.h. But those are hard to use until the preprocessor becomes available. +3) Try using the GC_DEBUG facilities. This is less likely to be successful here than if the collector crashes. +[The rest of these are primarily for wizards. You shouldn't need them unless you're doing something really strange, or debugging a collector port.] +4) Don't turn on incremental collection. If that fixes the problem, suspect a bug in the dirty bit implementation. Try compiling with -DCHECKSUMS to check for modified, but supposedly clean, pages. +5) On a SPARC, in a single-threaded environment, GC_print_callers(GC_arrays._last_stack) prints a cryptic stack trace as of the time of the last collection. (You will need a debugger to decipher the result.) The question to ask then is "why should this object have been accessible at the time of the last collection? Where was a pointer to it stored?". This facility should be easy to add for some other collector ports (namely if it's easy to traverse stack frames), but will be hard for others. +6) "print *GC_find_header(p)" in dbx or gdb will print the garbage collector block header information associated with the object p (e.g. object size, etc.) +7) GC_is_marked(p) determines whether p is the base address of a marked object. Note that objects allocated since the last collection should not be marked, and that unmarked objects are reclaimed incrementally. It's usually most interesting to set a breakpoint in GC_finish_collection and then to determine how much of the damaged data structure is marked at that point. +8) Look at the tracing facility in mark.c. (Ignore this suggestion unless you are very familiar with collector internals.) + + diff --git a/gc/README.dj b/gc/README.dj new file mode 100644 index 0000000..613bc42 --- /dev/null +++ b/gc/README.dj @@ -0,0 +1,12 @@ +[Original version supplied by Xiaokun Zhu <xiaokun@aero.gla.ac.uk>] +[This version came mostly from Gary Leavens. ] + +Look first at Makefile.dj, and possibly change the definitions of +RM and MV if you don't have rm and mv installed. +Then use Makefile.dj to compile the garbage collector. +For example, you can do: + + make -f Makefile.dj test + +All the tests should work fine. + diff --git a/gc/README.hp b/gc/README.hp new file mode 100644 index 0000000..b290590 --- /dev/null +++ b/gc/README.hp @@ -0,0 +1,11 @@ +Dynamic loading support requires that executables be linked with -ldld. +The alternative is to build the collector without defining DYNAMIC_LOADING +in gcconfig.h and ensuring that all garbage collectable objects are +accessible without considering statically allocated variables in dynamic +libraries. + +The collector should compile with either plain cc or cc -Ae. CC -Aa +fails to define _HPUX_SOURCE and thus will not configure the collector +correctly. + +There is currently no thread support. diff --git a/gc/README.linux b/gc/README.linux new file mode 100644 index 0000000..b4f136a --- /dev/null +++ b/gc/README.linux @@ -0,0 +1,50 @@ +See README.alpha for Linux on DEC AXP info. + +This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K +and PowerPC are also integrated. They should behave similarly, except that +the PowerPC port lacks incremental GC support, and it is unknown to what +extent the Linux threads code is functional. + +Incremental GC is supported on Intel IA32 and M68K. + +Dynamic libraries are supported on an ELF system. A static executable +should be linked with the gcc option "-Wl,-defsym,_DYNAMIC=0". + +The collector appears to work with Linux threads. We have seen +intermittent hangs in sem_wait. So far we have been unable to reproduce +these unless the process was being debugged or traced. Thus it's +possible that the only real issue is that the debugger loses +signals on rare occasions. + +The garbage collector uses SIGPWR and SIGXCPU if it is used with +Linux threads. These should not be touched by the client program. + +To use threads, you need to abide by the following requirements: + +1) You need to use LinuxThreads (which are included in libc6). + + The collector relies on some implementation details of the LinuxThreads + package. It is unlikely that this code will work on other + pthread implementations (in particular it will *not* work with + MIT pthreads). + +2) You must compile the collector with -DLINUX_THREADS and -D_REENTRANT + specified in the Makefile. + +3) Every file that makes thread calls should define LINUX_THREADS and + _REENTRANT and then include gc.h. Gc.h redefines some of the + pthread primitives as macros which also provide the collector with + information it requires. + +4) Currently dlopen() is probably not safe. The collector must traverse + the list of libraries maintained by the runtime loader. That can + probably be an inconsistent state when a thread calling the loader is + is stopped for GC. (It's possible that this is fixable in the + same way it is handled for SOLARIS_THREADS, with GC_dlopen.) + +5) The combination of LINUX_THREADS, REDIRECT_MALLOC, and incremental + collection fails in seemingly random places. This hasn't been tracked + down yet, but is perhaps not completely astonishing. The thread package + uses malloc, and thus can presumably get SIGSEGVs while inside the + package. There is no real guarantee that signals are handled properly + at that point. diff --git a/gc/README.rs6000 b/gc/README.rs6000 new file mode 100644 index 0000000..f5630b2 --- /dev/null +++ b/gc/README.rs6000 @@ -0,0 +1,9 @@ +We have so far failed to find a good way to determine the stack base. +It is highly recommended that GC_stackbottom be set explicitly on program +startup. The supplied value sometimes causes failure under AIX 4.1, though +it appears to work under 3.X. HEURISTIC2 seems to work under 4.1, but +involves a substantial performance penalty, and will fail if there is +no limit on stack size. + +There is no thread support. (I assume recent versions of AIX provide +pthreads? I no longer have access to a machine ...) diff --git a/gc/README.sgi b/gc/README.sgi new file mode 100644 index 0000000..e67124b --- /dev/null +++ b/gc/README.sgi @@ -0,0 +1,41 @@ +Performance of the incremental collector can be greatly enhanced with +-DNO_EXECUTE_PERMISSION. + +The collector should run with all of the -32, -n32 and -64 ABIs. Remember to +define the AS macro in the Makefile to be "as -64", or "as -n32". + +If you use -DREDIRECT_MALLOC=GC_malloc with C++ code, your code should make +at least one explicit call to malloc instead of new to ensure that the proper +version of malloc is linked in. + +Sproc threads are not supported in this version, though there may exist other +ports. + +Pthreads support is provided. This requires that: + +1) You compile the collector with -DIRIX_THREADS specified in the Makefile. + +2) You have the latest pthreads patches installed. + +(Though the collector makes only documented pthread calls, +it relies on signal/threads interactions working just right in ways +that are not required by the standard. It is unlikely that this code +will run on other pthreads platforms. But please tell me if it does.) + +3) Every file that makes thread calls should define IRIX_THREADS and then +include gc.h. Gc.h redefines some of the pthread primitives as macros which +also provide the collector with information it requires. + +4) pthread_cond_wait and pthread_cond_timed_wait should be prepared for +premature wakeups. (I believe the pthreads and realted standards require this +anyway. Irix pthreads often terminate a wait if a signal arrives. +The garbage collector uses signals to stop threads.) + +5) It is expensive to stop a thread waiting in IO at the time the request is +initiated. Applications with many such threads may not exhibit acceptable +performance with the collector. (Increasing the heap size may help.) + +6) The collector should not be compiled with -DREDIRECT_MALLOC. This +confuses some library calls made by the pthreads implementation, which +expect the standard malloc. + diff --git a/gc/README.solaris2 b/gc/README.solaris2 new file mode 100644 index 0000000..e593513 --- /dev/null +++ b/gc/README.solaris2 @@ -0,0 +1,65 @@ +The collector supports both incremental collection and threads under +Solaris 2. The incremental collector normally retrieves page dirty information +through the appropriate /proc calls. But it can also be configured +(by defining MPROTECT_VDB instead of PROC_VDB in gcconfig.h) to use mprotect +and signals. This may result in shorter pause times, but it is no longer +safe to issue arbitrary system calls that write to the heap. + +Under other UNIX versions, +the collector normally obtains memory through sbrk. There is some reason +to expect that this is not safe if the client program also calls the system +malloc, or especially realloc. The sbrk man page strongly suggests this is +not safe: "Many library routines use malloc() internally, so use brk() +and sbrk() only when you know that malloc() definitely will not be used by +any library routine." This doesn't make a lot of sense to me, since there +seems to be no documentation as to which routines can transitively call malloc. +Nonetheless, under Solaris2, the collector now (since 4.12) allocates +memory using mmap by default. (It defines USE_MMAP in gcconfig.h.) +You may want to reverse this decisions if you use -DREDIRECT_MALLOC=... + + +SOLARIS THREADS: + +The collector must be compiled with -DSOLARIS_THREADS to be thread safe. +It is also essential that gc.h be included in files that call thr_create, +thr_join, thr_suspend, thr_continue, or dlopen. Gc.h macro defines +these to also do GC bookkeeping, etc. Gc.h must be included with +SOLARIS_THREADS defined, otherwise these replacements are not visible. +A collector built in this way way only be used by programs that are +linked with the threads library. + +If you are using the Pthreads interface, also define _SOLARIS_PTHREADS. + +In this mode, the collector contains various workarounds for older Solaris +bugs. Mostly, these should not be noticeable unless you look at system +call traces. However, it cannot protect a guard page at the end of +a thread stack. If you know that you will only be running Solaris2.5 +or later, it should be possible to fix this by compiling the collector +with -DSOLARIS23_MPROTECT_BUG_FIXED. + +Jeremy Fitzhardinge points out that there is a problem with the dlopen +replacement, in that startup code in the library is run while the allocation +lock is held. This appears to be difficult to fix, since the collector does +look at data structures maintained by dlopen, and hence some locking is needed +around the dlopen call. Defining USE_PROC_FOR_LIBRARIES will get address +space layout information from /proc avoiding the dlopen lock. But this has +other disadvanatages, e.g. mmapped files may be scanned. + +If solaris_threads are used on an X86 processor with malloc redirected to +GC_malloc, it is necessary to call GC_thr_init explicitly before forking the +first thread. (This avoids a deadlock arising from calling GC_thr_init +with the allocation lock held.) + +It appears that there is a problem in using gc_cpp.h in conjunction with +Solaris threads and Sun's C++ runtime. Apparently the overloaded new operator +is invoked by some iostream initialization code before threads are correctly +initialized. As a result, call to thr_self() in garbage collector +initialization segfaults. Currently the only known workaround is to not +invoke the garbage collector from a user defined global operator new, or to +have it invoke the garbage-collector's allocators only after main has started. +(Note that the latter requires a moderately expensive test in operator +delete.) + +Hans-J. Boehm +(The above contains my personal opinions, which are probably not shared +by anyone else.) diff --git a/gc/README.uts b/gc/README.uts new file mode 100644 index 0000000..6be4966 --- /dev/null +++ b/gc/README.uts @@ -0,0 +1,2 @@ +Alistair Crooks supplied the port. He used Lexa C version 2.1.3 with +-Xa to compile. diff --git a/gc/README.win32 b/gc/README.win32 new file mode 100644 index 0000000..d78816b --- /dev/null +++ b/gc/README.win32 @@ -0,0 +1,149 @@ +The collector has only been compiled under Windows NT, with the +original Microsoft SDK, with Visual C++ 2.0 and later, with +the GNU win32 environment, with Borland 4.5, and recently with +Watcom C. + +It runs under both win32s and win32, but with different semantics. +Under win32, all writable pages outside of the heaps and stack are +scanned for roots. Thus the collector sees pointers in DLL data +segments. Under win32s, only the main data segment is scanned. +(The main data segment should always be scanned. Under some +versions of win32s, other regions may also be scanned.) +Thus all accessible objects should be accessible from local variables +or variables in the main data segment. Alternatively, other data +segments (e.g. in DLLs) may be registered with the collector by +calling GC_init() and then GC_register_root_section(a), where +a is the address of some variable inside the data segment. (Duplicate +registrations are ignored, but not terribly quickly.) + +(There are two reasons for this. We didn't want to see many 16:16 +pointers. And the VirtualQuery call has different semantics under +the two systems, and under different versions of win32s.) + +The collector test program "gctest" is linked as a GUI application, +but does not open any windows. Its output appears in the file +"gc.log". It may be started from the file manager. The hour glass +cursor will appear as long as it's running. If it is started from the +command line, it will usually run in the background. Wait a few +minutes (a few seconds on a modern machine) before you check the output. +You should see either a failure indication or a "Collector appears to +work" message. + +The cord test program has not been ported (but should port +easily). A toy editor (cord/de.exe) based on cords (heavyweight +strings represented as trees) has been ported and is included. +It runs fine under either win32 or win32S. It serves as an example +of a true Windows application, except that it was written by a +nonexpert Windows programmer. (There are some peculiarities +in the way files are displayed. The <cr> is displayed explicitly +for standard DOS text files. As in the UNIX version, control +characters are displayed explicitly, but in this case as red text. +This may be suboptimal for some tastes and/or sets of default +window colors.) + +For Microsoft development tools, rename NT_MAKEFILE as +MAKEFILE. (Make sure that the CPU environment variable is defined +to be i386.) + +For GNU-win32, use the regular makefile, possibly after uncommenting +the line "include Makefile.DLLs". The latter should be necessary only +if you want to package the collector as a DLL. The GNU-win32 port is +believed to work only for b18, not b19, probably dues to linker changes +in b19. This is probably fixable with a different definition of +DATASTART and DATAEND in gcconfig.h. + +For Borland tools, use BCC_MAKEFILE. Note that +Borland's compiler defaults to 1 byte alignment in structures (-a1), +whereas Visual C++ appears to default to 8 byte alignment (/Zp8). +The garbage collector in its default configuration EXPECTS AT +LEAST 4 BYTE ALIGNMENT. Thus the BORLAND DEFAULT MUST +BE OVERRIDDEN. (In my opinion, it should usually be anyway. +I expect that -a1 introduces major performance penalties on a +486 or Pentium.) Note that this changes structure layouts. (As a last +resort, gcconfig.h can be changed to allow 1 byte alignment. But +this has significant negative performance implications.) +The Makefile is set up to assume Borland 4.5. If you have another +version, change the line near the top. By default, it does not +require the assembler. If you do have the assembler, I recommend +removing the -DUSE_GENERIC. + +Incremental collection support was recently added. This is +currently pretty simpleminded. Pages are protected. Protection +faults are caught by a handler installed at the bottom of the handler +stack. This is both slow and interacts poorly with a debugger. +Whenever possible, I recommend adding a call to +GC_enable_incremental at the last possible moment, after most +debugging is complete. Unlike the UNIX versions, no system +calls are wrapped by the collector itself. It may be necessary +to wrap ReadFile calls that use a buffer in the heap, so that the +call does not encounter a protection fault while it's running. +(As usual, none of this is an issue unless GC_enable_incremental +is called.) + +Note that incremental collection is disabled with -DSMALL_CONFIG, +which is the default for win32. If you need incremental collection, +undefine SMALL_CONFIG. + +Incremental collection is not supported under win32s, and it may not +be possible to do so. However, win32 applications that attempt to use +incremental collection should continue to run, since the +collector detects if it's running under win32s and turns calls to +GC_enable_incremental() into noops. + +James Clark has contributed the necessary code to support win32 threads. +This code is known to exhibit some problems with incremental collection +enabled. Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE +to build this version. Note that this requires some files whose names +are more than 8 + 3 characters long. Thus you should unpack the tar file +so that long file names are preserved. To build the garbage collector +test with VC++ from the command line, use + +nmake /F ".\gc.mak" CFG="gctest - Win32 Release" + +This requires that the subdirectory gctest\Release exist. +The test program and DLL will reside in the Release directory. + +This version relies on the collector residing in a dll. + +This version currently supports incremental collection only if it is +enabled before any additional threads are created. +Version 4.13 attempts to fix some of the earlier problems, but there +may be other issues. If you need solid support for win32 threads, you +might check with Geodesic Systems. Their collector must be licensed, +but they have invested far more time in win32-specific issues. + +Hans + +Ivan V. Demakov's README for the Watcom port: + +The collector has been compiled with Watcom C 10.6 and 11.0. +It runs under win32, win32s, and even under msdos with dos4gw +dos-extender. It should also run under OS/2, though this isn't +tested. Under win32 the collector can be built either as dll +or as static library. + +Note that all compilations were done under Windows 95 or NT. +For unknown reason compiling under Windows 3.11 for NT (one +attempt has been made) leads to broken executables. + +Incremental collection is not supported. + +cord is not ported. + +Before compiling you may need to edit WCC_MAKEFILE to set target +platform, library type (dynamic or static), calling conventions, and +optimization options. + +To compile the collector and testing programs use the command: + wmake -f WCC_MAKEFILE + +All programs using gc should be compiled with 4-byte alignment. +For further explanations on this see comments about Borland. + +If gc compiled as dll, the macro ``GC_DLL'' should be defined before +including "gc.h" (for example, with -DGC_DLL compiler option). It's +important, otherwise resulting programs will not run. + +Ivan Demakov (email: ivan@tgrad.nsk.su) + + diff --git a/gc/SCoptions.amiga b/gc/SCoptions.amiga new file mode 100644 index 0000000..a61e0cb --- /dev/null +++ b/gc/SCoptions.amiga @@ -0,0 +1,16 @@ +CPU=68030 +NOSTACKCHECK +OPTIMIZE +VERBOSE +MAPHUNK +NOVERSION +NOICONS +OPTIMIZERTIME +DEFINE SILENT +DEFINE AMIGA_SKIP_SEG +IGNORE=85 +IGNORE=154 +IGNORE=161 +IGNORE=100 +OPTIMIZERCOMPLEXITY=4 +OPTIMIZERDEPTH=3 diff --git a/gc/SMakefile.amiga b/gc/SMakefile.amiga new file mode 100644 index 0000000..e9602c0 --- /dev/null +++ b/gc/SMakefile.amiga @@ -0,0 +1,48 @@ +OBJS= alloc.o reclaim.o allchblk.o misc.o mach_dep.o os_dep.o mark_rts.o headers.o mark.o obj_map.o blacklst.o finalize.o new_hblk.o real_malloc.o dyn_load.o dbg_mlc.o malloc.o stubborn.o checksums.o typd_mlc.o ptr_chck.o + +INC= gc_private.h gc_hdrs.h gc.h gcconfig.h + +all: gctest setjmp_t + +alloc.o : alloc.c $(INC) +reclaim.o : reclaim.c $(INC) +allchblk.o : allchblk.c $(INC) +misc.o : misc.c $(INC) +os_dep.o : os_dep.c $(INC) +mark_rts.o : mark_rts.c $(INC) +headers.o : headers.c $(INC) +mark.o : mark.c $(INC) +obj_map.o : obj_map.c $(INC) +blacklst.o : blacklst.c $(INC) +finalize.o : finalize.c $(INC) + sc noopt finalize.c # There seems to be a bug in the optimizer (V6.51). + # gctest won't work if you remove this... +new_hblk.o : new_hblk.c $(INC) +real_malloc.o : real_malloc.c $(INC) +dyn_load.o : dyn_load.c $(INC) +dbg_mlc.o : dbg_mlc.c $(INC) +malloc.o : malloc.c $(INC) +mallocx.o : malloc.c $(INC) +stubborn.o : stubborn.c $(INC) +checksums.o : checksums.c $(INC) +typd_mlc.o: typd_mlc.c $(INC) +mach_dep.o : mach_dep.c $(INC) +ptr_chck.o: ptr_chck.c $(INC) +test.o : test.c $(INC) + +gc.lib: $(OBJS) + oml gc.lib r $(OBJS) + +clean: + delete gc.lib gctest setjmp_t \#?.o + +gctest: gc.lib test.o + slink LIB:c.o test.o to $@ lib gc.lib LIB:sc.lib LIB:scm.lib + +setjmp_t: setjmp_t.c gc.h + sc setjmp_t.c + slink LIB:c.o $@.o to $@ lib LIB:sc.lib + +test: setjmp_t gctest + setjmp_t + gctest diff --git a/gc/WCC_MAKEFILE b/gc/WCC_MAKEFILE new file mode 100644 index 0000000..087ff6a --- /dev/null +++ b/gc/WCC_MAKEFILE @@ -0,0 +1,196 @@ +# Makefile for Watcom C/C++ 10.5, 10.6, 11.0 on NT, OS2 and DOS4GW. +# May work with Watcom 10.0. + +# Uncoment one of the lines below for cross compilation. +SYSTEM=MSWIN32 +#SYSTEM=DOS4GW +#SYSTEM=OS2 + +# The collector can be built either as dynamic or as static library. +# Select the library type you need. +#MAKE_AS_DLL=1 +MAKE_AS_LIB=1 + +# Select calling conventions. +# Possible choices are r and s. +CALLING=s + +# Select target CPU. +# Possible choices are 3, 4, 5, and 6. +# The last choice available only since version 11.0. +CPU=5 + +# Set optimization options. +# Watcom before 11.0 does not support option "-oh". +OPTIM=-oneatx -s +#OPTIM=-ohneatx -s + +DEFS=-DALL_INTERIOR_POINTERS -DSILENT -DNO_SIGNALS #-DSMALL_CONFIG #-DGC_DEBUG + + +##### + +!ifndef SYSTEM +!ifdef __MSDOS__ +SYSTEM=DOS4GW +!else ifdef __NT__ +SYSTEM=MSWIN32 +!else ifdef __OS2__ +SYSTEM=OS2 +!else +SYSTEM=Unknown +!endif +!endif + +!define $(SYSTEM) + +!ifdef DOS4GW +SYSFLAG=-DDOS4GW -bt=dos +!else ifdef MSWIN32 +SYSFLAG=-DMSWIN32 -bt=nt +!else ifdef OS2 +SYSFLAG=-DOS2 -bt=os2 +!else +!error undefined or unsupported target platform: $(SYSTEM) +!endif +!ifdef MAKE_AS_DLL +DLLFLAG=-bd -DGC_DLL +TEST_DLLFLAG=-DGC_DLL +!else ifdef MAKE_AS_LIB +DLLFLAG= +TEST_DLLFLAG= +!else +!error Either MAKE_AS_LIB or MAKE_AS_DLL should be defined +!endif + +CC=wcc386 +CXX=wpp386 + +# -DUSE_GENERIC is required ! +CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(DLLFLAG) -DGC_BUILD -DUSE_GENERIC $(DEFS) +CXXFLAGS= $(CFLAGS) +TEST_CFLAGS=-$(CPU)$(CALLING) $(OPTIM) -zp4 -zc $(SYSFLAG) $(TEST_DLLFLAG) $(DEFS) +TEST_CXXFLAGS= $(TEST_CFLAGS) + +OBJS= alloc.obj reclaim.obj allchblk.obj misc.obj & + mach_dep.obj os_dep.obj mark_rts.obj headers.obj mark.obj & + obj_map.obj blacklst.obj finalize.obj new_hblk.obj & + dbg_mlc.obj malloc.obj stubborn.obj dyn_load.obj & + typd_mlc.obj ptr_chck.obj mallocx.obj + +all: gc.lib gctest.exe test_cpp.exe + +!ifdef MAKE_AS_DLL + +gc.lib: gc.dll gc_cpp.obj + *wlib -b -c -n -p=512 $@ +gc.dll +gc_cpp.obj + +gc.dll: $(OBJS) .AUTODEPEND + @%create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys os2v2_dll +!else ifdef MSWIN32 + @%append $*.lnk sys nt_dll +!else ifdef OS2 + @%append $*.lnk sys os2v2_dll +!endif + @%append $*.lnk name $* + @for %i in ($(OBJS)) do @%append $*.lnk file '%i' +!ifeq CALLING s + @%append $*.lnk export GC_is_marked + @%append $*.lnk export GC_incr_words_allocd + @%append $*.lnk export GC_incr_mem_freed + @%append $*.lnk export GC_generic_malloc_words_small +!else + @%append $*.lnk export GC_is_marked_ + @%append $*.lnk export GC_incr_words_allocd_ + @%append $*.lnk export GC_incr_mem_freed_ + @%append $*.lnk export GC_generic_malloc_words_small_ +!endif + *wlink @$*.lnk +!else +gc.lib: $(OBJS) gc_cpp.obj + @%create $*.lb1 + @for %i in ($(OBJS)) do @%append $*.lb1 +'%i' + @%append $*.lb1 +'gc_cpp.obj' + *wlib -b -c -n -p=512 $@ @$*.lb1 + +!endif + + +gctest.exe: test.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test.obj + @%append $*.lnk library gc.lib +!ifdef MAKE_AS_DLL +!ifeq CALLING s + @%append $*.lnk import GC_is_marked gc +!else + @%append $*.lnk import GC_is_marked_ gc +!endif +!endif + *wlink @$*.lnk +test_cpp.exe: test_cpp.obj gc.lib + %create $*.lnk +!ifdef DOS4GW + @%append $*.lnk sys dos4g +!else ifdef MSWIN32 + @%append $*.lnk sys nt +!else ifdef OS2 + @%append $*.lnk sys os2v2 +!endif + @%append $*.lnk op case + @%append $*.lnk op stack=256K + @%append $*.lnk name $* + @%append $*.lnk file test_cpp.obj + @%append $*.lnk library gc.lib +!ifdef MAKE_AS_DLL +!ifeq CALLING s + @%append $*.lnk import GC_incr_words_allocd gc + @%append $*.lnk import GC_incr_mem_freed gc + @%append $*.lnk import GC_generic_malloc_words_small gc +!else + @%append $*.lnk import GC_incr_words_allocd_ gc + @%append $*.lnk import GC_incr_mem_freed_ gc + @%append $*.lnk import GC_generic_malloc_words_small_ gc +!endif +!endif + *wlink @$*.lnk + +gc_cpp.obj: gc_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc +test.obj: test.c .AUTODEPEND + $(CC) $(TEST_CFLAGS) $*.c +test_cpp.obj: test_cpp.cc .AUTODEPEND + $(CXX) $(TEST_CXXFLAGS) -iinclude $*.cc + + +.c.obj: .AUTODEPEND + $(CC) $(CFLAGS) $*.c + +.cc.obj: .AUTODEPEND + $(CXX) $(CXXFLAGS) $*.cc + +clean : .SYMBOLIC + @if exist *.obj del *.obj + @if exist *.map del *.map + @if exist *.lnk del *.lnk + @if exist *.lb1 del *.lb1 + @if exist *.sym del *.sym + @if exist *.err del *.err + @if exist *.tmp del *.tmp + @if exist *.lst del *.lst + @if exist *.exe del *.exe + @if exist *.log del *.log + @if exist *.lib del *.lib + @if exist *.dll del *.dll diff --git a/gc/add_gc_prefix.c b/gc/add_gc_prefix.c new file mode 100644 index 0000000..0d1ab6d --- /dev/null +++ b/gc/add_gc_prefix.c @@ -0,0 +1,14 @@ +# include <stdio.h> + +int main(argc, argv, envp) +int argc; +char ** argv; +char ** envp; +{ + int i; + + for (i = 1; i < argc; i++) { + printf("gc/%s ", argv[i]); + } + return(0); +} diff --git a/gc/allchblk.c b/gc/allchblk.c new file mode 100644 index 0000000..d8d0afd --- /dev/null +++ b/gc/allchblk.c @@ -0,0 +1,726 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998-1999 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#define DEBUG +#undef DEBUG +#include <stdio.h> +#include "gc_priv.h" + + +/* + * Free heap blocks are kept on one of several free lists, + * depending on the size of the block. Each free list is doubly linked. + * Adjacent free blocks are coalesced. + */ + + +# define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE) + /* largest block we will allocate starting on a black */ + /* listed block. Must be >= HBLKSIZE. */ + + +# define UNIQUE_THRESHOLD 32 + /* Sizes up to this many HBLKs each have their own free list */ +# define HUGE_THRESHOLD 256 + /* Sizes of at least this many heap blocks are mapped to a */ + /* single free list. */ +# define FL_COMPRESSION 8 + /* In between sizes map this many distinct sizes to a single */ + /* bin. */ + +# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \ + + UNIQUE_THRESHOLD + +struct hblk * GC_hblkfreelist[N_HBLK_FLS+1] = { 0 }; + +/* Map a number of blocks to the appropriate large block free list index. */ +int GC_hblk_fl_from_blocks(blocks_needed) +word blocks_needed; +{ + if (blocks_needed <= UNIQUE_THRESHOLD) return blocks_needed; + if (blocks_needed >= HUGE_THRESHOLD) return N_HBLK_FLS; + return (blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION + + UNIQUE_THRESHOLD; + +} + +# define HBLK_IS_FREE(hdr) ((hdr) -> hb_map == GC_invalid_map) +# define PHDR(hhdr) HDR(hhdr -> hb_prev) +# define NHDR(hhdr) HDR(hhdr -> hb_next) + +# ifdef USE_MUNMAP +# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0) +# else /* !USE_MMAP */ +# define IS_MAPPED(hhdr) 1 +# endif /* USE_MUNMAP */ + +# if !defined(NO_DEBUGGING) +void GC_print_hblkfreelist() +{ + struct hblk * h; + word total_free = 0; + hdr * hhdr; + word sz; + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + h = GC_hblkfreelist[i]; + if (0 != h) GC_printf1("Free list %ld:\n", (unsigned long)i); + while (h != 0) { + hhdr = HDR(h); + sz = hhdr -> hb_sz; + GC_printf2("\t0x%lx size %lu ", (unsigned long)h, (unsigned long)sz); + total_free += sz; + if (GC_is_black_listed(h, HBLKSIZE) != 0) { + GC_printf0("start black listed\n"); + } else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0) { + GC_printf0("partially black listed\n"); + } else { + GC_printf0("not black listed\n"); + } + h = hhdr -> hb_next; + } + } + if (total_free != GC_large_free_bytes) { + GC_printf1("GC_large_free_bytes = %lu (INCONSISTENT!!)\n", + (unsigned long) GC_large_free_bytes); + } + GC_printf1("Total of %lu bytes on free list\n", (unsigned long)total_free); +} + +/* Return the free list index on which the block described by the header */ +/* appears, or -1 if it appears nowhere. */ +int free_list_index_of(wanted) +hdr * wanted; +{ + struct hblk * h; + hdr * hhdr; + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + h = GC_hblkfreelist[i]; + while (h != 0) { + hhdr = HDR(h); + if (hhdr == wanted) return i; + h = hhdr -> hb_next; + } + } + return -1; +} + +void GC_dump_regions() +{ + int i; + ptr_t start, end; + ptr_t p; + size_t bytes; + hdr *hhdr; + for (i = 0; i < GC_n_heap_sects; ++i) { + start = GC_heap_sects[i].hs_start; + bytes = GC_heap_sects[i].hs_bytes; + end = start + bytes; + /* Merge in contiguous sections. */ + while (i+1 < GC_n_heap_sects && GC_heap_sects[i+1].hs_start == end) { + ++i; + end = GC_heap_sects[i].hs_start + GC_heap_sects[i].hs_bytes; + } + GC_printf2("***Section from 0x%lx to 0x%lx\n", start, end); + for (p = start; p < end;) { + hhdr = HDR(p); + GC_printf1("\t0x%lx ", (unsigned long)p); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + GC_printf1("Missing header!!\n", hhdr); + p += HBLKSIZE; + continue; + } + if (HBLK_IS_FREE(hhdr)) { + int correct_index = GC_hblk_fl_from_blocks( + divHBLKSZ(hhdr -> hb_sz)); + int actual_index; + + GC_printf1("\tfree block of size 0x%lx bytes", + (unsigned long)(hhdr -> hb_sz)); + if (IS_MAPPED(hhdr)) { + GC_printf0("\n"); + } else { + GC_printf0("(unmapped)\n"); + } + actual_index = free_list_index_of(hhdr); + if (-1 == actual_index) { + GC_printf1("\t\tBlock not on free list %ld!!\n", + correct_index); + } else if (correct_index != actual_index) { + GC_printf2("\t\tBlock on list %ld, should be on %ld!!\n", + actual_index, correct_index); + } + p += hhdr -> hb_sz; + } else { + GC_printf1("\tused for blocks of size 0x%lx bytes\n", + (unsigned long)WORDS_TO_BYTES(hhdr -> hb_sz)); + p += HBLKSIZE * OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + } + } +} + +# endif /* NO_DEBUGGING */ + +/* Initialize hdr for a block containing the indicated size and */ +/* kind of objects. */ +/* Return FALSE on failure. */ +static GC_bool setup_header(hhdr, sz, kind, flags) +register hdr * hhdr; +word sz; /* object size in words */ +int kind; +unsigned char flags; +{ + register word descr; + + /* Add description of valid object pointers */ + if (!GC_add_map_entry(sz)) return(FALSE); + hhdr -> hb_map = GC_obj_map[sz > MAXOBJSZ? 0 : sz]; + + /* Set size, kind and mark proc fields */ + hhdr -> hb_sz = sz; + hhdr -> hb_obj_kind = kind; + hhdr -> hb_flags = flags; + descr = GC_obj_kinds[kind].ok_descriptor; + if (GC_obj_kinds[kind].ok_relocate_descr) descr += WORDS_TO_BYTES(sz); + hhdr -> hb_descr = descr; + + /* Clear mark bits */ + GC_clear_hdr_marks(hhdr); + + hhdr -> hb_last_reclaimed = (unsigned short)GC_gc_no; + return(TRUE); +} + +#define FL_UNKNOWN -1 +/* + * Remove hhdr from the appropriate free list. + * We assume it is on the nth free list, or on the size + * appropriate free list if n is FL_UNKNOWN. + */ +void GC_remove_from_fl(hhdr, n) +hdr * hhdr; +int n; +{ + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + if (hhdr -> hb_prev == 0) { + int index; + if (FL_UNKNOWN == n) { + index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz)); + } else { + index = n; + } + GC_ASSERT(HDR(GC_hblkfreelist[index]) == hhdr); + GC_hblkfreelist[index] = hhdr -> hb_next; + } else { + PHDR(hhdr) -> hb_next = hhdr -> hb_next; + } + if (0 != hhdr -> hb_next) { + GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr))); + NHDR(hhdr) -> hb_prev = hhdr -> hb_prev; + } +} + +/* + * Return a pointer to the free block ending just before h, if any. + */ +struct hblk * GC_free_block_ending_at(h) +struct hblk *h; +{ + struct hblk * p = h - 1; + hdr * phdr = HDR(p); + + while (0 != phdr && IS_FORWARDING_ADDR_OR_NIL(phdr)) { + p = FORWARDED_ADDR(p,phdr); + phdr = HDR(p); + } + if (0 != phdr && HBLK_IS_FREE(phdr)) return p; + p = GC_prev_block(h - 1); + if (0 != p) { + phdr = HDR(p); + if (HBLK_IS_FREE(phdr) && (ptr_t)p + phdr -> hb_sz == (ptr_t)h) { + return p; + } + } + return 0; +} + +/* + * Add hhdr to the appropriate free list. + * We maintain individual free lists sorted by address. + */ +void GC_add_to_fl(h, hhdr) +struct hblk *h; +hdr * hhdr; +{ + int index = GC_hblk_fl_from_blocks(divHBLKSZ(hhdr -> hb_sz)); + struct hblk *second = GC_hblkfreelist[index]; +# ifdef GC_ASSERTIONS + struct hblk *next = (struct hblk *)((word)h + hhdr -> hb_sz); + hdr * nexthdr = HDR(next); + struct hblk *prev = GC_free_block_ending_at(h); + hdr * prevhdr = HDR(prev); + GC_ASSERT(nexthdr == 0 || !HBLK_IS_FREE(nexthdr) || !IS_MAPPED(nexthdr)); + GC_ASSERT(prev == 0 || !HBLK_IS_FREE(prevhdr) || !IS_MAPPED(prevhdr)); +# endif + GC_ASSERT(((hhdr -> hb_sz) & (HBLKSIZE-1)) == 0); + GC_hblkfreelist[index] = h; + hhdr -> hb_next = second; + hhdr -> hb_prev = 0; + if (0 != second) HDR(second) -> hb_prev = h; + GC_invalidate_map(hhdr); +} + +#ifdef USE_MUNMAP + +/* Unmap blocks that haven't been recently touched. This is the only way */ +/* way blocks are ever unmapped. */ +void GC_unmap_old(void) +{ + struct hblk * h; + hdr * hhdr; + word sz; + unsigned short last_rec, threshold; + int i; +# define UNMAP_THRESHOLD 6 + + for (i = 0; i <= N_HBLK_FLS; ++i) { + for (h = GC_hblkfreelist[i]; 0 != h; h = hhdr -> hb_next) { + hhdr = HDR(h); + if (!IS_MAPPED(hhdr)) continue; + threshold = (unsigned short)(GC_gc_no - UNMAP_THRESHOLD); + last_rec = hhdr -> hb_last_reclaimed; + if (last_rec > GC_gc_no + || last_rec < threshold && threshold < GC_gc_no + /* not recently wrapped */) { + sz = hhdr -> hb_sz; + GC_unmap((ptr_t)h, sz); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } + } +} + +/* Merge all unmapped blocks that are adjacent to other free */ +/* blocks. This may involve remapping, since all blocks are either */ +/* fully mapped or fully unmapped. */ +void GC_merge_unmapped(void) +{ + struct hblk * h, *next; + hdr * hhdr, *nexthdr; + word size, nextsize; + int i; + + for (i = 0; i <= N_HBLK_FLS; ++i) { + h = GC_hblkfreelist[i]; + while (h != 0) { + hhdr = HDR(h); + size = hhdr->hb_sz; + next = (struct hblk *)((word)h + size); + nexthdr = HDR(next); + /* Coalesce with successor, if possible */ + if (0 != nexthdr && HBLK_IS_FREE(nexthdr)) { + nextsize = nexthdr -> hb_sz; + if (IS_MAPPED(hhdr)) { + GC_ASSERT(!IS_MAPPED(nexthdr)); + /* make both consistent, so that we can merge */ + if (size > nextsize) { + GC_remap((ptr_t)next, nextsize); + } else { + GC_unmap((ptr_t)h, size); + hhdr -> hb_flags |= WAS_UNMAPPED; + } + } else if (IS_MAPPED(nexthdr)) { + GC_ASSERT(!IS_MAPPED(hhdr)); + if (size > nextsize) { + GC_unmap((ptr_t)next, nextsize); + } else { + GC_remap((ptr_t)h, size); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + } + } else { + /* Unmap any gap in the middle */ + GC_unmap_gap((ptr_t)h, size, (ptr_t)next, nexthdr -> hb_sz); + } + /* If they are both unmapped, we merge, but leave unmapped. */ + GC_remove_from_fl(hhdr, i); + GC_remove_from_fl(nexthdr, FL_UNKNOWN); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + GC_add_to_fl(h, hhdr); + /* Start over at beginning of list */ + h = GC_hblkfreelist[i]; + } else /* not mergable with successor */ { + h = hhdr -> hb_next; + } + } /* while (h != 0) ... */ + } /* for ... */ +} + +#endif /* USE_MUNMAP */ + +/* + * Return a pointer to a block starting at h of length bytes. + * Memory for the block is mapped. + * Remove the block from its free list, and return the remainder (if any) + * to its appropriate free list. + * May fail by returning 0. + * The header for the returned block must be set up by the caller. + * If the return value is not 0, then hhdr is the header for it. + */ +struct hblk * GC_get_first_part(h, hhdr, bytes, index) +struct hblk *h; +hdr * hhdr; +word bytes; +int index; +{ + word total_size = hhdr -> hb_sz; + struct hblk * rest; + hdr * rest_hdr; + + GC_ASSERT((total_size & (HBLKSIZE-1)) == 0); + GC_remove_from_fl(hhdr, index); + if (total_size == bytes) return h; + rest = (struct hblk *)((word)h + bytes); + if (!GC_install_header(rest)) return(0); + rest_hdr = HDR(rest); + rest_hdr -> hb_sz = total_size - bytes; + rest_hdr -> hb_flags = 0; +# ifdef GC_ASSERTIONS + // Mark h not free, to avoid assertion about adjacent free blocks. + hhdr -> hb_map = 0; +# endif + GC_add_to_fl(rest, rest_hdr); + return h; +} + +/* + * H is a free block. N points at an address inside it. + * A new header for n has already been set up. Fix up h's header + * to reflect the fact that it is being split, move it to the + * appropriate free list. + * N replaces h in the original free list. + * + * Nhdr is not completely filled in, since it is about to allocated. + * It may in fact end up on the wrong free list for its size. + * (Hence adding it to a free list is silly. But this path is hopefully + * rare enough that it doesn't matter. The code is cleaner this way.) + */ +void GC_split_block(h, hhdr, n, nhdr, index) +struct hblk *h; +hdr * hhdr; +struct hblk *n; +hdr * nhdr; +int index; /* Index of free list */ +{ + word total_size = hhdr -> hb_sz; + word h_size = (word)n - (word)h; + struct hblk *prev = hhdr -> hb_prev; + struct hblk *next = hhdr -> hb_next; + + /* Replace h with n on its freelist */ + nhdr -> hb_prev = prev; + nhdr -> hb_next = next; + nhdr -> hb_sz = total_size - h_size; + nhdr -> hb_flags = 0; + if (0 != prev) { + HDR(prev) -> hb_next = n; + } else { + GC_hblkfreelist[index] = n; + } + if (0 != next) { + HDR(next) -> hb_prev = n; + } +# ifdef GC_ASSERTIONS + nhdr -> hb_map = 0; /* Don't fail test for consecutive */ + /* free blocks in GC_add_to_fl. */ +# endif +# ifdef USE_MUNMAP + hhdr -> hb_last_reclaimed = GC_gc_no; +# endif + hhdr -> hb_sz = h_size; + GC_add_to_fl(h, hhdr); + GC_invalidate_map(nhdr); +} + +struct hblk * GC_allochblk_nth(); + +/* + * Allocate (and return pointer to) a heap block + * for objects of size sz words, searching the nth free list. + * + * NOTE: We set obj_map field in header correctly. + * Caller is responsible for building an object freelist in block. + * + * We clear the block if it is destined for large objects, and if + * kind requires that newly allocated objects be cleared. + */ +struct hblk * +GC_allochblk(sz, kind, flags) +word sz; +int kind; +unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ +{ + int start_list = GC_hblk_fl_from_blocks(OBJ_SZ_TO_BLOCKS(sz)); + int i; + for (i = start_list; i <= N_HBLK_FLS; ++i) { + struct hblk * result = GC_allochblk_nth(sz, kind, flags, i); + if (0 != result) return result; + } + return 0; +} +/* + * The same, but with search restricted to nth free list. + */ +struct hblk * +GC_allochblk_nth(sz, kind, flags, n) +word sz; +int kind; +unsigned char flags; /* IGNORE_OFF_PAGE or 0 */ +int n; +{ + register struct hblk *hbp; + register hdr * hhdr; /* Header corr. to hbp */ + register struct hblk *thishbp; + register hdr * thishdr; /* Header corr. to hbp */ + signed_word size_needed; /* number of bytes in requested objects */ + signed_word size_avail; /* bytes available in this block */ + + size_needed = HBLKSIZE * OBJ_SZ_TO_BLOCKS(sz); + + /* search for a big enough block in free list */ + hbp = GC_hblkfreelist[n]; + hhdr = HDR(hbp); + for(; 0 != hbp; hbp = hhdr -> hb_next, hhdr = HDR(hbp)) { + size_avail = hhdr->hb_sz; + if (size_avail < size_needed) continue; +# ifdef PRESERVE_LAST + if (size_avail != size_needed + && !GC_incremental && GC_should_collect()) { + continue; + } +# endif + /* If the next heap block is obviously better, go on. */ + /* This prevents us from disassembling a single large block */ + /* to get tiny blocks. */ + { + signed_word next_size; + + thishbp = hhdr -> hb_next; + if (thishbp != 0) { + thishdr = HDR(thishbp); + next_size = (signed_word)(thishdr -> hb_sz); + if (next_size < size_avail + && next_size >= size_needed + && !GC_is_black_listed(thishbp, (word)size_needed)) { + continue; + } + } + } + if ( !IS_UNCOLLECTABLE(kind) && + (kind != PTRFREE || size_needed > MAX_BLACK_LIST_ALLOC)) { + struct hblk * lasthbp = hbp; + ptr_t search_end = (ptr_t)hbp + size_avail - size_needed; + signed_word orig_avail = size_avail; + signed_word eff_size_needed = ((flags & IGNORE_OFF_PAGE)? + HBLKSIZE + : size_needed); + + + while ((ptr_t)lasthbp <= search_end + && (thishbp = GC_is_black_listed(lasthbp, + (word)eff_size_needed))) { + lasthbp = thishbp; + } + size_avail -= (ptr_t)lasthbp - (ptr_t)hbp; + thishbp = lasthbp; + if (size_avail >= size_needed) { + if (thishbp != hbp && GC_install_header(thishbp)) { + /* Make sure it's mapped before we mangle it. */ +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_remap((ptr_t)hbp, size_avail); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + } +# endif + /* Split the block at thishbp */ + thishdr = HDR(thishbp); + GC_split_block(hbp, hhdr, thishbp, thishdr, n); + /* Advance to thishbp */ + hbp = thishbp; + hhdr = thishdr; + /* We must now allocate thishbp, since it may */ + /* be on the wrong free list. */ + } + } else if (size_needed > (signed_word)BL_LIMIT + && orig_avail - size_needed + > (signed_word)BL_LIMIT) { + /* Punt, since anything else risks unreasonable heap growth. */ + WARN("Needed to allocate blacklisted block at 0x%lx\n", + (word)hbp); + size_avail = orig_avail; + } else if (size_avail == 0 && size_needed == HBLKSIZE + && IS_MAPPED(hhdr)) { + if (!GC_find_leak) { + static unsigned count = 0; + + /* The block is completely blacklisted. We need */ + /* to drop some such blocks, since otherwise we spend */ + /* all our time traversing them if pointerfree */ + /* blocks are unpopular. */ + /* A dropped block will be reconsidered at next GC. */ + if ((++count & 3) == 0) { + /* Allocate and drop the block in small chunks, to */ + /* maximize the chance that we will recover some */ + /* later. */ + word total_size = hhdr -> hb_sz; + struct hblk * limit = hbp + divHBLKSZ(total_size); + struct hblk * h; + struct hblk * prev = hhdr -> hb_prev; + + GC_words_wasted += total_size; + GC_large_free_bytes -= total_size; + GC_remove_from_fl(hhdr, n); + for (h = hbp; h < limit; h++) { + if (h == hbp || GC_install_header(h)) { + hhdr = HDR(h); + (void) setup_header( + hhdr, + BYTES_TO_WORDS(HBLKSIZE - HDR_BYTES), + PTRFREE, 0); /* Cant fail */ + if (GC_debugging_started) { + BZERO(h + HDR_BYTES, HBLKSIZE - HDR_BYTES); + } + } + } + /* Restore hbp to point at free block */ + hbp = prev; + if (0 == hbp) { + return GC_allochblk_nth(sz, kind, flags, n); + } + hhdr = HDR(hbp); + } + } + } + } + if( size_avail >= size_needed ) { +# ifdef USE_MUNMAP + if (!IS_MAPPED(hhdr)) { + GC_remap((ptr_t)hbp, size_avail); + hhdr -> hb_flags &= ~WAS_UNMAPPED; + } +# endif + /* hbp may be on the wrong freelist; the parameter n */ + /* is important. */ + hbp = GC_get_first_part(hbp, hhdr, size_needed, n); + break; + } + } + + if (0 == hbp) return 0; + + /* Notify virtual dirty bit implementation that we are about to write. */ + GC_write_hint(hbp); + + /* Add it to map of valid blocks */ + if (!GC_install_counts(hbp, (word)size_needed)) return(0); + /* This leaks memory under very rare conditions. */ + + /* Set up header */ + if (!setup_header(hhdr, sz, kind, flags)) { + GC_remove_counts(hbp, (word)size_needed); + return(0); /* ditto */ + } + + /* Clear block if necessary */ + if (GC_debugging_started + || sz > MAXOBJSZ && GC_obj_kinds[kind].ok_init) { + BZERO(hbp + HDR_BYTES, size_needed - HDR_BYTES); + } + + /* We just successfully allocated a block. Restart count of */ + /* consecutive failures. */ + { + extern unsigned GC_fail_count; + + GC_fail_count = 0; + } + + GC_large_free_bytes -= size_needed; + + GC_ASSERT(IS_MAPPED(hhdr)); + return( hbp ); +} + +struct hblk * GC_freehblk_ptr = 0; /* Search position hint for GC_freehblk */ + +/* + * Free a heap block. + * + * Coalesce the block with its neighbors if possible. + * + * All mark words are assumed to be cleared. + */ +void +GC_freehblk(hbp) +struct hblk *hbp; +{ +struct hblk *next, *prev; +hdr *hhdr, *prevhdr, *nexthdr; +signed_word size; + + + hhdr = HDR(hbp); + size = hhdr->hb_sz; + size = HBLKSIZE * OBJ_SZ_TO_BLOCKS(size); + GC_remove_counts(hbp, (word)size); + hhdr->hb_sz = size; + + /* Check for duplicate deallocation in the easy case */ + if (HBLK_IS_FREE(hhdr)) { + GC_printf1("Duplicate large block deallocation of 0x%lx\n", + (unsigned long) hbp); + } + + GC_ASSERT(IS_MAPPED(hhdr)); + GC_invalidate_map(hhdr); + next = (struct hblk *)((word)hbp + size); + nexthdr = HDR(next); + prev = GC_free_block_ending_at(hbp); + /* Coalesce with successor, if possible */ + if(0 != nexthdr && HBLK_IS_FREE(nexthdr) && IS_MAPPED(nexthdr)) { + GC_remove_from_fl(nexthdr, FL_UNKNOWN); + hhdr -> hb_sz += nexthdr -> hb_sz; + GC_remove_header(next); + } + /* Coalesce with predecessor, if possible. */ + if (0 != prev) { + prevhdr = HDR(prev); + if (IS_MAPPED(prevhdr)) { + GC_remove_from_fl(prevhdr, FL_UNKNOWN); + prevhdr -> hb_sz += hhdr -> hb_sz; + GC_remove_header(hbp); + hbp = prev; + hhdr = prevhdr; + } + } + + GC_large_free_bytes += size; + GC_add_to_fl(hbp, hhdr); +} + diff --git a/gc/alloc.c b/gc/alloc.c new file mode 100644 index 0000000..1c57951 --- /dev/null +++ b/gc/alloc.c @@ -0,0 +1,884 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + +# include "gc_priv.h" + +# include <stdio.h> +# ifndef MACOS +# include <signal.h> +# include <sys/types.h> +# endif + +/* + * Separate free lists are maintained for different sized objects + * up to MAXOBJSZ. + * The call GC_allocobj(i,k) ensures that the freelist for + * kind k objects of size i points to a non-empty + * free list. It returns a pointer to the first entry on the free list. + * In a single-threaded world, GC_allocobj may be called to allocate + * an object of (small) size i as follows: + * + * opp = &(GC_objfreelist[i]); + * if (*opp == 0) GC_allocobj(i, NORMAL); + * ptr = *opp; + * *opp = obj_link(ptr); + * + * Note that this is very fast if the free list is non-empty; it should + * only involve the execution of 4 or 5 simple instructions. + * All composite objects on freelists are cleared, except for + * their first word. + */ + +/* + * The allocator uses GC_allochblk to allocate large chunks of objects. + * These chunks all start on addresses which are multiples of + * HBLKSZ. Each allocated chunk has an associated header, + * which can be located quickly based on the address of the chunk. + * (See headers.c for details.) + * This makes it possible to check quickly whether an + * arbitrary address corresponds to an object administered by the + * allocator. + */ + +word GC_non_gc_bytes = 0; /* Number of bytes not intended to be collected */ + +word GC_gc_no = 0; + +#ifndef SMALL_CONFIG + int GC_incremental = 0; /* By default, stop the world. */ +#endif + +int GC_full_freq = 4; /* Every 5th collection is a full */ + /* collection. */ + +char * GC_copyright[] = +{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", +"Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. ", +"Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. ", +"THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY", +" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.", +"See source code for details." }; + +# include "version.h" + +/* some more variables */ + +extern signed_word GC_mem_found; /* Number of reclaimed longwords */ + /* after garbage collection */ + +GC_bool GC_dont_expand = 0; + +word GC_free_space_divisor = 3; + +extern GC_bool GC_collection_in_progress(); + /* Collection is in progress, or was abandoned. */ + +int GC_never_stop_func GC_PROTO((void)) { return(0); } + +CLOCK_TYPE GC_start_time; /* Time at which we stopped world. */ + /* used only in GC_timeout_stop_func. */ + +int GC_n_attempts = 0; /* Number of attempts at finishing */ + /* collection within TIME_LIMIT */ + +#ifdef SMALL_CONFIG +# define GC_timeout_stop_func GC_never_stop_func +#else + int GC_timeout_stop_func GC_PROTO((void)) + { + CLOCK_TYPE current_time; + static unsigned count = 0; + unsigned long time_diff; + + if ((count++ & 3) != 0) return(0); + GET_TIME(current_time); + time_diff = MS_TIME_DIFF(current_time,GC_start_time); + if (time_diff >= TIME_LIMIT) { +# ifdef PRINTSTATS + GC_printf0("Abandoning stopped marking after "); + GC_printf1("%lu msecs", (unsigned long)time_diff); + GC_printf1("(attempt %d)\n", (unsigned long) GC_n_attempts); +# endif + return(1); + } + return(0); + } +#endif /* !SMALL_CONFIG */ + +/* Return the minimum number of words that must be allocated between */ +/* collections to amortize the collection cost. */ +static word min_words_allocd() +{ +# ifdef THREADS + /* We punt, for now. */ + register signed_word stack_size = 10000; +# else + int dummy; + register signed_word stack_size = (ptr_t)(&dummy) - GC_stackbottom; +# endif + word total_root_size; /* includes double stack size, */ + /* since the stack is expensive */ + /* to scan. */ + word scan_size; /* Estimate of memory to be scanned */ + /* during normal GC. */ + + if (stack_size < 0) stack_size = -stack_size; + total_root_size = 2 * stack_size + GC_root_size; + scan_size = BYTES_TO_WORDS(GC_heapsize - GC_large_free_bytes + + (GC_large_free_bytes >> 2) + /* use a bit more of large empty heap */ + + total_root_size); + if (GC_incremental) { + return scan_size / (2 * GC_free_space_divisor); + } else { + return scan_size / GC_free_space_divisor; + } +} + +/* Return the number of words allocated, adjusted for explicit storage */ +/* management, etc.. This number is used in deciding when to trigger */ +/* collections. */ +word GC_adj_words_allocd() +{ + register signed_word result; + register signed_word expl_managed = + BYTES_TO_WORDS((long)GC_non_gc_bytes + - (long)GC_non_gc_bytes_at_gc); + + /* Don't count what was explicitly freed, or newly allocated for */ + /* explicit management. Note that deallocating an explicitly */ + /* managed object should not alter result, assuming the client */ + /* is playing by the rules. */ + result = (signed_word)GC_words_allocd + - (signed_word)GC_mem_freed - expl_managed; + if (result > (signed_word)GC_words_allocd) { + result = GC_words_allocd; + /* probably client bug or unfortunate scheduling */ + } + result += GC_words_finalized; + /* We count objects enqueued for finalization as though they */ + /* had been reallocated this round. Finalization is user */ + /* visible progress. And if we don't count this, we have */ + /* stability problems for programs that finalize all objects. */ + result += GC_words_wasted; + /* This doesn't reflect useful work. But if there is lots of */ + /* new fragmentation, the same is probably true of the heap, */ + /* and the collection will be correspondingly cheaper. */ + if (result < (signed_word)(GC_words_allocd >> 3)) { + /* Always count at least 1/8 of the allocations. We don't want */ + /* to collect too infrequently, since that would inhibit */ + /* coalescing of free storage blocks. */ + /* This also makes us partially robust against client bugs. */ + return(GC_words_allocd >> 3); + } else { + return(result); + } +} + + +/* Clear up a few frames worth of garbage left at the top of the stack. */ +/* This is used to prevent us from accidentally treating garbade left */ +/* on the stack by other parts of the collector as roots. This */ +/* differs from the code in misc.c, which actually tries to keep the */ +/* stack clear of long-lived, client-generated garbage. */ +void GC_clear_a_few_frames() +{ +# define NWORDS 64 + word frames[NWORDS]; + register int i; + + for (i = 0; i < NWORDS; i++) frames[i] = 0; +} + +/* Have we allocated enough to amortize a collection? */ +GC_bool GC_should_collect() +{ + return(GC_adj_words_allocd() >= min_words_allocd()); +} + +void GC_notify_full_gc() +{ + if (GC_start_call_back != (void (*)())0) { + (*GC_start_call_back)(); + } +} + +/* + * Initiate a garbage collection if appropriate. + * Choose judiciously + * between partial, full, and stop-world collections. + * Assumes lock held, signals disabled. + */ +void GC_maybe_gc() +{ + static int n_partial_gcs = 0; + GC_bool is_full_gc = FALSE; + + if (GC_should_collect()) { + if (!GC_incremental) { + GC_notify_full_gc(); + GC_gcollect_inner(); + n_partial_gcs = 0; + return; + } else if (n_partial_gcs >= GC_full_freq) { +# ifdef PRINTSTATS + GC_printf2( + "***>Full mark for collection %lu after %ld allocd bytes\n", + (unsigned long) GC_gc_no+1, + (long)WORDS_TO_BYTES(GC_words_allocd)); +# endif + GC_promote_black_lists(); + (void)GC_reclaim_all((GC_stop_func)0, TRUE); + GC_clear_marks(); + n_partial_gcs = 0; + GC_notify_full_gc(); + is_full_gc = TRUE; + } else { + n_partial_gcs++; + } + /* We try to mark with the world stopped. */ + /* If we run out of time, this turns into */ + /* incremental marking. */ + GET_TIME(GC_start_time); + if (GC_stopped_mark(GC_timeout_stop_func)) { +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + GC_finish_collection(); + } else { + if (!is_full_gc) { + /* Count this as the first attempt */ + GC_n_attempts++; + } + } + } +} + + +/* + * Stop the world garbage collection. Assumes lock held, signals disabled. + * If stop_func is not GC_never_stop_func, then abort if stop_func returns TRUE. + */ +GC_bool GC_try_to_collect_inner(stop_func) +GC_stop_func stop_func; +{ + if (GC_incremental && GC_collection_in_progress()) { +# ifdef PRINTSTATS + GC_printf0( + "GC_try_to_collect_inner: finishing collection in progress\n"); +# endif /* PRINTSTATS */ + /* Just finish collection already in progress. */ + while(GC_collection_in_progress()) { + if (stop_func()) return(FALSE); + GC_collect_a_little_inner(1); + } + } +# ifdef PRINTSTATS + GC_printf2( + "Initiating full world-stop collection %lu after %ld allocd bytes\n", + (unsigned long) GC_gc_no+1, + (long)WORDS_TO_BYTES(GC_words_allocd)); +# endif + GC_promote_black_lists(); + /* Make sure all blocks have been reclaimed, so sweep routines */ + /* don't see cleared mark bits. */ + /* If we're guaranteed to finish, then this is unnecessary. */ + if (stop_func != GC_never_stop_func + && !GC_reclaim_all(stop_func, FALSE)) { + /* Aborted. So far everything is still consistent. */ + return(FALSE); + } + GC_invalidate_mark_state(); /* Flush mark stack. */ + GC_clear_marks(); +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + if (!GC_stopped_mark(stop_func)) { + if (!GC_incremental) { + /* We're partially done and have no way to complete or use */ + /* current work. Reestablish invariants as cheaply as */ + /* possible. */ + GC_invalidate_mark_state(); + GC_unpromote_black_lists(); + } /* else we claim the world is already still consistent. We'll */ + /* finish incrementally. */ + return(FALSE); + } + GC_finish_collection(); + return(TRUE); +} + + + +/* + * Perform n units of garbage collection work. A unit is intended to touch + * roughly GC_RATE pages. Every once in a while, we do more than that. + * This needa to be a fairly large number with our current incremental + * GC strategy, since otherwise we allocate too much during GC, and the + * cleanup gets expensive. + */ +# define GC_RATE 10 +# define MAX_PRIOR_ATTEMPTS 1 + /* Maximum number of prior attempts at world stop marking */ + /* A value of 1 means that we finish the seconf time, no matter */ + /* how long it takes. Doesn't count the initial root scan */ + /* for a full GC. */ + +int GC_deficit = 0; /* The number of extra calls to GC_mark_some */ + /* that we have made. */ + +void GC_collect_a_little_inner(n) +int n; +{ + register int i; + + if (GC_incremental && GC_collection_in_progress()) { + for (i = GC_deficit; i < GC_RATE*n; i++) { + if (GC_mark_some((ptr_t)0)) { + /* Need to finish a collection */ +# ifdef SAVE_CALL_CHAIN + GC_save_callers(GC_last_stack); +# endif + if (GC_n_attempts < MAX_PRIOR_ATTEMPTS) { + GET_TIME(GC_start_time); + if (!GC_stopped_mark(GC_timeout_stop_func)) { + GC_n_attempts++; + break; + } + } else { + (void)GC_stopped_mark(GC_never_stop_func); + } + GC_finish_collection(); + break; + } + } + if (GC_deficit > 0) GC_deficit -= GC_RATE*n; + if (GC_deficit < 0) GC_deficit = 0; + } else { + GC_maybe_gc(); + } +} + +int GC_collect_a_little GC_PROTO(()) +{ + int result; + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + GC_collect_a_little_inner(1); + result = (int)GC_collection_in_progress(); + UNLOCK(); + ENABLE_SIGNALS(); + return(result); +} + +/* + * Assumes lock is held, signals are disabled. + * We stop the world. + * If stop_func() ever returns TRUE, we may fail and return FALSE. + * Increment GC_gc_no if we succeed. + */ +GC_bool GC_stopped_mark(stop_func) +GC_stop_func stop_func; +{ + register int i; + int dummy; +# ifdef PRINTSTATS + CLOCK_TYPE start_time, current_time; +# endif + + STOP_WORLD(); +# ifdef PRINTSTATS + GET_TIME(start_time); + GC_printf1("--> Marking for collection %lu ", + (unsigned long) GC_gc_no + 1); + GC_printf2("after %lu allocd bytes + %lu wasted bytes\n", + (unsigned long) WORDS_TO_BYTES(GC_words_allocd), + (unsigned long) WORDS_TO_BYTES(GC_words_wasted)); +# endif + + /* Mark from all roots. */ + /* Minimize junk left in my registers and on the stack */ + GC_clear_a_few_frames(); + GC_noop(0,0,0,0,0,0); + GC_initiate_gc(); + for(i = 0;;i++) { + if ((*stop_func)()) { +# ifdef PRINTSTATS + GC_printf0("Abandoned stopped marking after "); + GC_printf1("%lu iterations\n", + (unsigned long)i); +# endif + GC_deficit = i; /* Give the mutator a chance. */ + START_WORLD(); + return(FALSE); + } + if (GC_mark_some((ptr_t)(&dummy))) break; + } + + GC_gc_no++; +# ifdef PRINTSTATS + GC_printf2("Collection %lu reclaimed %ld bytes", + (unsigned long) GC_gc_no - 1, + (long)WORDS_TO_BYTES(GC_mem_found)); + GC_printf1(" ---> heapsize = %lu bytes\n", + (unsigned long) GC_heapsize); + /* Printf arguments may be pushed in funny places. Clear the */ + /* space. */ + GC_printf0(""); +# endif + + /* Check all debugged objects for consistency */ + if (GC_debugging_started) { + (*GC_check_heap)(); + } + +# ifdef PRINTTIMES + GET_TIME(current_time); + GC_printf1("World-stopped marking took %lu msecs\n", + MS_TIME_DIFF(current_time,start_time)); +# endif + START_WORLD(); + return(TRUE); +} + + +/* Finish up a collection. Assumes lock is held, signals are disabled, */ +/* but the world is otherwise running. */ +void GC_finish_collection() +{ +# ifdef PRINTTIMES + CLOCK_TYPE start_time; + CLOCK_TYPE finalize_time; + CLOCK_TYPE done_time; + + GET_TIME(start_time); + finalize_time = start_time; +# endif + +# ifdef GATHERSTATS + GC_mem_found = 0; +# endif + if (GC_find_leak) { + /* Mark all objects on the free list. All objects should be */ + /* marked when we're done. */ + { + register word size; /* current object size */ + register ptr_t p; /* pointer to current object */ + register struct hblk * h; /* pointer to block containing *p */ + register hdr * hhdr; + register int word_no; /* "index" of *p in *q */ + int kind; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJSZ; size++) { + for (p= GC_obj_kinds[kind].ok_freelist[size]; + p != 0; p=obj_link(p)){ + h = HBLKPTR(p); + hhdr = HDR(h); + word_no = (((word *)p) - ((word *)h)); + set_mark_bit_from_hdr(hhdr, word_no); + } + } + } + } + GC_start_reclaim(TRUE); + /* The above just checks; it doesn't really reclaim anything. */ + } + + GC_finalize(); +# ifdef STUBBORN_ALLOC + GC_clean_changing_list(); +# endif + +# ifdef PRINTTIMES + GET_TIME(finalize_time); +# endif + + /* Clear free list mark bits, in case they got accidentally marked */ + /* Note: HBLKPTR(p) == pointer to head of block containing *p */ + /* (or GC_find_leak is set and they were intentionally marked.) */ + /* Also subtract memory remaining from GC_mem_found count. */ + /* Note that composite objects on free list are cleared. */ + /* Thus accidentally marking a free list is not a problem; only */ + /* objects on the list itself will be marked, and that's fixed here. */ + { + register word size; /* current object size */ + register ptr_t p; /* pointer to current object */ + register struct hblk * h; /* pointer to block containing *p */ + register hdr * hhdr; + register int word_no; /* "index" of *p in *q */ + int kind; + + for (kind = 0; kind < GC_n_kinds; kind++) { + for (size = 1; size <= MAXOBJSZ; size++) { + for (p= GC_obj_kinds[kind].ok_freelist[size]; + p != 0; p=obj_link(p)){ + h = HBLKPTR(p); + hhdr = HDR(h); + word_no = (((word *)p) - ((word *)h)); + clear_mark_bit_from_hdr(hhdr, word_no); +# ifdef GATHERSTATS + GC_mem_found -= size; +# endif + } + } + } + } + + +# ifdef PRINTSTATS + GC_printf1("Bytes recovered before sweep - f.l. count = %ld\n", + (long)WORDS_TO_BYTES(GC_mem_found)); +# endif + /* Reconstruct free lists to contain everything not marked */ + GC_start_reclaim(FALSE); + +# ifdef PRINTSTATS + GC_printf2( + "Immediately reclaimed %ld bytes in heap of size %lu bytes", + (long)WORDS_TO_BYTES(GC_mem_found), + (unsigned long)GC_heapsize); +# ifdef USE_MUNMAP + GC_printf1("(%lu unmapped)", GC_unmapped_bytes); +# endif + GC_printf2( + "\n%lu (atomic) + %lu (composite) collectable bytes in use\n", + (unsigned long)WORDS_TO_BYTES(GC_atomic_in_use), + (unsigned long)WORDS_TO_BYTES(GC_composite_in_use)); +# endif + + GC_n_attempts = 0; + /* Reset or increment counters for next cycle */ + GC_words_allocd_before_gc += GC_words_allocd; + GC_non_gc_bytes_at_gc = GC_non_gc_bytes; + GC_words_allocd = 0; + GC_words_wasted = 0; + GC_mem_freed = 0; + +# ifdef USE_MUNMAP + GC_unmap_old(); +# endif +# ifdef PRINTTIMES + GET_TIME(done_time); + GC_printf2("Finalize + initiate sweep took %lu + %lu msecs\n", + MS_TIME_DIFF(finalize_time,start_time), + MS_TIME_DIFF(done_time,finalize_time)); +# endif +} + +/* Externally callable routine to invoke full, stop-world collection */ +# if defined(__STDC__) || defined(__cplusplus) + int GC_try_to_collect(GC_stop_func stop_func) +# else + int GC_try_to_collect(stop_func) + GC_stop_func stop_func; +# endif +{ + int result; + DCL_LOCK_STATE; + + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + ENTER_GC(); + if (!GC_is_initialized) GC_init_inner(); + /* Minimize junk left in my registers */ + GC_noop(0,0,0,0,0,0); + result = (int)GC_try_to_collect_inner(stop_func); + EXIT_GC(); + UNLOCK(); + ENABLE_SIGNALS(); + if(result) GC_INVOKE_FINALIZERS(); + return(result); +} + +void GC_gcollect GC_PROTO(()) +{ + GC_notify_full_gc(); + (void)GC_try_to_collect(GC_never_stop_func); +} + +word GC_n_heap_sects = 0; /* Number of sections currently in heap. */ + +/* + * Use the chunk of memory starting at p of size bytes as part of the heap. + * Assumes p is HBLKSIZE aligned, and bytes is a multiple of HBLKSIZE. + */ +void GC_add_to_heap(p, bytes) +struct hblk *p; +word bytes; +{ + word words; + hdr * phdr; + + if (GC_n_heap_sects >= MAX_HEAP_SECTS) { + ABORT("Too many heap sections: Increase MAXHINCR or MAX_HEAP_SECTS"); + } + if (!GC_install_header(p)) { + /* This is extremely unlikely. Can't add it. This will */ + /* almost certainly result in a 0 return from the allocator, */ + /* which is entirely appropriate. */ + return; + } + GC_heap_sects[GC_n_heap_sects].hs_start = (ptr_t)p; + GC_heap_sects[GC_n_heap_sects].hs_bytes = bytes; + GC_n_heap_sects++; + words = BYTES_TO_WORDS(bytes - HDR_BYTES); + phdr = HDR(p); + phdr -> hb_sz = words; + phdr -> hb_map = (char *)1; /* A value != GC_invalid_map */ + phdr -> hb_flags = 0; + GC_freehblk(p); + GC_heapsize += bytes; + if ((ptr_t)p <= GC_least_plausible_heap_addr + || GC_least_plausible_heap_addr == 0) { + GC_least_plausible_heap_addr = (ptr_t)p - sizeof(word); + /* Making it a little smaller than necessary prevents */ + /* us from getting a false hit from the variable */ + /* itself. There's some unintentional reflection */ + /* here. */ + } + if ((ptr_t)p + bytes >= GC_greatest_plausible_heap_addr) { + GC_greatest_plausible_heap_addr = (ptr_t)p + bytes; + } +} + +# if !defined(NO_DEBUGGING) +void GC_print_heap_sects() +{ + register unsigned i; + + GC_printf1("Total heap size: %lu\n", (unsigned long) GC_heapsize); + for (i = 0; i < GC_n_heap_sects; i++) { + unsigned long start = (unsigned long) GC_heap_sects[i].hs_start; + unsigned long len = (unsigned long) GC_heap_sects[i].hs_bytes; + struct hblk *h; + unsigned nbl = 0; + + GC_printf3("Section %ld from 0x%lx to 0x%lx ", (unsigned long)i, + start, (unsigned long)(start + len)); + for (h = (struct hblk *)start; h < (struct hblk *)(start + len); h++) { + if (GC_is_black_listed(h, HBLKSIZE)) nbl++; + } + GC_printf2("%lu/%lu blacklisted\n", (unsigned long)nbl, + (unsigned long)(len/HBLKSIZE)); + } +} +# endif + +ptr_t GC_least_plausible_heap_addr = (ptr_t)ONES; +ptr_t GC_greatest_plausible_heap_addr = 0; + +ptr_t GC_max(x,y) +ptr_t x, y; +{ + return(x > y? x : y); +} + +ptr_t GC_min(x,y) +ptr_t x, y; +{ + return(x < y? x : y); +} + +# if defined(__STDC__) || defined(__cplusplus) + void GC_set_max_heap_size(GC_word n) +# else + void GC_set_max_heap_size(n) + GC_word n; +# endif +{ + GC_max_heapsize = n; +} + +GC_word GC_max_retries = 0; + +/* + * this explicitly increases the size of the heap. It is used + * internally, but may also be invoked from GC_expand_hp by the user. + * The argument is in units of HBLKSIZE. + * Tiny values of n are rounded up. + * Returns FALSE on failure. + */ +GC_bool GC_expand_hp_inner(n) +word n; +{ + word bytes; + struct hblk * space; + word expansion_slop; /* Number of bytes by which we expect the */ + /* heap to expand soon. */ + + if (n < MINHINCR) n = MINHINCR; + bytes = n * HBLKSIZE; + /* Make sure bytes is a multiple of GC_page_size */ + { + word mask = GC_page_size - 1; + bytes += mask; + bytes &= ~mask; + } + + if (GC_max_heapsize != 0 && GC_heapsize + bytes > GC_max_heapsize) { + /* Exceeded self-imposed limit */ + return(FALSE); + } + space = GET_MEM(bytes); + if( space == 0 ) { + return(FALSE); + } +# ifdef PRINTSTATS + GC_printf2("Increasing heap size by %lu after %lu allocated bytes\n", + (unsigned long)bytes, + (unsigned long)WORDS_TO_BYTES(GC_words_allocd)); +# ifdef UNDEFINED + GC_printf1("Root size = %lu\n", GC_root_size); + GC_print_block_list(); GC_print_hblkfreelist(); + GC_printf0("\n"); +# endif +# endif + expansion_slop = 8 * WORDS_TO_BYTES(min_words_allocd()); + if (5 * HBLKSIZE * MAXHINCR > expansion_slop) { + expansion_slop = 5 * HBLKSIZE * MAXHINCR; + } + if (GC_last_heap_addr == 0 && !((word)space & SIGNB) + || GC_last_heap_addr != 0 && GC_last_heap_addr < (ptr_t)space) { + /* Assume the heap is growing up */ + GC_greatest_plausible_heap_addr = + GC_max(GC_greatest_plausible_heap_addr, + (ptr_t)space + bytes + expansion_slop); + } else { + /* Heap is growing down */ + GC_least_plausible_heap_addr = + GC_min(GC_least_plausible_heap_addr, + (ptr_t)space - expansion_slop); + } + GC_prev_heap_addr = GC_last_heap_addr; + GC_last_heap_addr = (ptr_t)space; + GC_add_to_heap(space, bytes); + return(TRUE); +} + +/* Really returns a bool, but it's externally visible, so that's clumsy. */ +/* Arguments is in bytes. */ +# if defined(__STDC__) || defined(__cplusplus) + int GC_expand_hp(size_t bytes) +# else + int GC_expand_hp(bytes) + size_t bytes; +# endif +{ + int result; + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + if (!GC_is_initialized) GC_init_inner(); + result = (int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); + UNLOCK(); + ENABLE_SIGNALS(); + return(result); +} + +unsigned GC_fail_count = 0; + /* How many consecutive GC/expansion failures? */ + /* Reset by GC_allochblk. */ + +GC_bool GC_collect_or_expand(needed_blocks, ignore_off_page) +word needed_blocks; +GC_bool ignore_off_page; +{ + if (!GC_incremental && !GC_dont_gc && GC_should_collect()) { + GC_notify_full_gc(); + GC_gcollect_inner(); + } else { + word blocks_to_get = GC_heapsize/(HBLKSIZE*GC_free_space_divisor) + + needed_blocks; + + if (blocks_to_get > MAXHINCR) { + word slop; + + if (ignore_off_page) { + slop = 4; + } else { + slop = 2*divHBLKSZ(BL_LIMIT); + if (slop > needed_blocks) slop = needed_blocks; + } + if (needed_blocks + slop > MAXHINCR) { + blocks_to_get = needed_blocks + slop; + } else { + blocks_to_get = MAXHINCR; + } + } + if (!GC_expand_hp_inner(blocks_to_get) + && !GC_expand_hp_inner(needed_blocks)) { + if (GC_fail_count++ < GC_max_retries) { + WARN("Out of Memory! Trying to continue ...\n", 0); + GC_notify_full_gc(); + GC_gcollect_inner(); + } else { + WARN("Out of Memory! Returning NIL!\n", 0); + return(FALSE); + } + } else { +# ifdef PRINTSTATS + if (GC_fail_count) { + GC_printf0("Memory available again ...\n"); + } +# endif + } + } + return(TRUE); +} + +/* + * Make sure the object free list for sz is not empty. + * Return a pointer to the first object on the free list. + * The object MUST BE REMOVED FROM THE FREE LIST BY THE CALLER. + * Assumes we hold the allocator lock and signals are disabled. + * + */ +ptr_t GC_allocobj(sz, kind) +word sz; +int kind; +{ + register ptr_t * flh = &(GC_obj_kinds[kind].ok_freelist[sz]); + + if (sz == 0) return(0); + + while (*flh == 0) { + ENTER_GC(); + /* Do our share of marking work */ + if(GC_incremental && !GC_dont_gc) GC_collect_a_little_inner(1); + /* Sweep blocks for objects of this size */ + GC_continue_reclaim(sz, kind); + EXIT_GC(); + if (*flh == 0) { + GC_new_hblk(sz, kind); + } + if (*flh == 0) { + ENTER_GC(); + if (!GC_collect_or_expand((word)1,FALSE)) { + EXIT_GC(); + return(0); + } + EXIT_GC(); + } + } + + return(*flh); +} diff --git a/gc/alpha_mach_dep.s b/gc/alpha_mach_dep.s new file mode 100644 index 0000000..0aaa97b --- /dev/null +++ b/gc/alpha_mach_dep.s @@ -0,0 +1,60 @@ + # $Id: alpha_mach_dep.s,v 1.1 2001/11/08 05:17:06 a-ito Exp $ + +# define call_push(x) \ + lda $16, 0(x); /* copy x to first argument register */ \ + jsr $26, GC_push_one; /* call GC_push_one, ret addr in $26 */ \ + ldgp $gp, 0($26) /* restore $gp register from $ra */ + + .text + .align 4 + .globl GC_push_regs + .ent GC_push_regs 2 +GC_push_regs: + ldgp $gp, 0($27) # set gp from the procedure value reg + lda $sp, -32($sp) # make stack frame + stq $26, 8($sp) # save return address + .mask 0x04000000, -8 + .frame $sp, 16, $26, 0 + + # call_push($0) # expression eval and int func result + + # call_push($1) # temp regs - not preserved cross calls + # call_push($2) + # call_push($3) + # call_push($4) + # call_push($5) + # call_push($6) + # call_push($7) + # call_push($8) + + call_push($9) # Saved regs + call_push($10) + call_push($11) + call_push($12) + call_push($13) + call_push($14) + + call_push($15) # frame ptr or saved reg + + # call_push($16) # argument regs - not preserved cross calls + # call_push($17) + # call_push($18) + # call_push($19) + # call_push($20) + # call_push($21) + + # call_push($22) # temp regs - not preserved cross calls + # call_push($23) + # call_push($24) + # call_push($25) + + # call_push($26) # return address - expression eval + # call_push($27) # procedure value or temporary reg + # call_push($28) # assembler temp - not presrved + call_push($29) # Global Pointer + # call_push($30) # Stack Pointer + + ldq $26, 8($sp) # restore return address + lda $sp, 32($sp) # pop stack frame + ret $31, ($26), 1 # return ($31 == hardwired zero) + .end GC_push_regs diff --git a/gc/backptr.h b/gc/backptr.h new file mode 100644 index 0000000..d34224e --- /dev/null +++ b/gc/backptr.h @@ -0,0 +1,56 @@ +/* + * This is a simple API to implement pointer back tracing, i.e. + * to answer questions such as "who is pointing to this" or + * "why is this object being retained by the collector" + * + * This API assumes that we have an ANSI C compiler. + * + * Most of these calls yield useful information on only after + * a garbage collection. Usually the client will first force + * a full collection and then gather information, preferably + * before much intervening allocation. + * + * The implementation of the interface is only about 99.9999% + * correct. It is intended to be good enough for profiling, + * but is not intended to be used with production code. + * + * Results are likely to be much more useful if all allocation is + * accomplished through the debugging allocators. + * + * The implementation idea is due to A. Demers. + */ + +/* Store information about the object referencing dest in *base_p */ +/* and *offset_p. */ +/* If multiple objects or roots point to dest, the one reported */ +/* will be the last on used by the garbage collector to trace the */ +/* object. */ +/* source is root ==> *base_p = address, *offset_p = 0 */ +/* source is heap object ==> *base_p != 0, *offset_p = offset */ +/* Returns 1 on success, 0 if source couldn't be determined. */ +/* Dest can be any address within a heap object. */ +typedef enum { GC_UNREFERENCED, /* No refence info available. */ + GC_NO_SPACE, /* Dest not allocated with debug alloc */ + GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p */ + GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */ + GC_FINALIZER_REFD /* Finalizable and hence accessible. */ +} GC_ref_kind; + +GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p); + +/* Generate a random heap address. */ +/* The resulting address is in the heap, but */ +/* not necessarily inside a valid object. */ +void * GC_generate_random_heap_address(void); + +/* Generate a random address inside a valid marked heap object. */ +void * GC_generate_random_valid_address(void); + +/* Force a garbage collection and generate a backtrace from a */ +/* random heap address. */ +/* This uses the GC logging mechanism (GC_printf) to produce */ +/* output. It can often be called from a debugger. The */ +/* source in dbg_mlc.c also serves as a sample client. */ +void GC_generate_random_backtrace(void); + + diff --git a/gc/barrett_diagram b/gc/barrett_diagram new file mode 100644 index 0000000..27e80dc --- /dev/null +++ b/gc/barrett_diagram @@ -0,0 +1,106 @@ +This is an ASCII diagram of the data structure used to check pointer +validity. It was provided by Dave Barrett <barrett@asgard.cs.colorado.edu>, +and should be of use to others attempting to understand the code. +The data structure in GC4.X is essentially the same. -HB + + + + + Data Structure used by GC_base in gc3.7: + 21-Apr-94 + + + + + 63 LOG_TOP_SZ[11] LOG_BOTTOM_SZ[10] LOG_HBLKSIZE[13] + +------------------+----------------+------------------+------------------+ + p:| | TL_HASH(hi) | | HBLKDISPL(p) | + +------------------+----------------+------------------+------------------+ + \-----------------------HBLKPTR(p)-------------------/ + \------------hi-------------------/ + \______ ________/ \________ _______/ \________ _______/ + V V V + | | | + GC_top_index[] | | | + --- +--------------+ | | | + ^ | | | | | + | | | | | | + TOP +--------------+<--+ | | + _SZ +-<| [] | * | | +(items)| +--------------+ if 0 < bi< HBLKSIZE | | + | | | | then large object | | + | | | | starts at the bi'th | | + v | | | HBLK before p. | i | + --- | +--------------+ | (word- | + v | aligned) | + bi= |GET_BI(p){->hash_link}->key==hi | | + v | | + | (bottom_index) \ scratch_alloc'd | | + | ( struct bi ) / by get_index() | | + --- +->+--------------+ | | + ^ | | | | + ^ | | | | + BOTTOM | | ha=GET_HDR_ADDR(p) | | +_SZ(items)+--------------+<----------------------+ +-------+ + | +--<| index[] | | + | | +--------------+ GC_obj_map: v + | | | | from / +-+-+-----+-+-+-+-+ --- + v | | | GC_add < 0| | | | | | | | ^ + --- | +--------------+ _map_entry \ +-+-+-----+-+-+-+-+ | + | | asc_link | +-+-+-----+-+-+-+-+ MAXOBJSZ + | +--------------+ +-->| | | j | | | | | +1 + | | key | | +-+-+-----+-+-+-+-+ | + | +--------------+ | +-+-+-----+-+-+-+-+ | + | | hash_link | | | | | | | | | | v + | +--------------+ | +-+-+-----+-+-+-+-+ --- + | | |<--MAX_OFFSET--->| + | | (bytes) +HDR(p)| GC_find_header(p) | |<--MAP_ENTRIES-->| + | \ from | =HBLKSIZE/WORDSZ + | (hdr) (struct hblkhdr) / alloc_hdr() | (1024 on Alpha) + +-->+----------------------+ | (8/16 bits each) +GET_HDR(p)| word hb_sz (words) | | + +----------------------+ | + | struct hblk *hb_next | | + +----------------------+ | + |mark_proc hb_mark_proc| | + +----------------------+ | + | char * hb_map |>-------------+ + +----------------------+ + | ushort hb_obj_kind | + +----------------------+ + | hb_last_reclaimed | + --- +----------------------+ + ^ | | + MARK_BITS| hb_marks[] | *if hdr is free, hb_sz + DISCARD_WORDS +_SZ(words)| | is the size of a heap chunk (struct hblk) + v | | of at least MININCR*HBLKSIZE bytes (below), + --- +----------------------+ otherwise, size of each object in chunk. + +Dynamic data structures above are interleaved throughout the heap in blocks of +size MININCR * HBLKSIZE bytes as done by gc_scratch_alloc which cannot be +freed; free lists are used (e.g. alloc_hdr). HBLKs's below are collected. + + (struct hblk) + --- +----------------------+ < HBLKSIZE --- --- DISCARD_ + ^ |garbage[DISCARD_WORDS]| aligned ^ ^ HDR_BYTES WORDS + | | | | v (bytes) (words) + | +-----hb_body----------+ < WORDSZ | --- --- + | | | aligned | ^ ^ + | | Object 0 | | hb_sz | + | | | i |(word- (words)| + | | | (bytes)|aligned) v | + | + - - - - - - - - - - -+ --- | --- | + | | | ^ | ^ | + n * | | j (words) | hb_sz BODY_SZ + HBLKSIZE | Object 1 | v v | (words) + (bytes) | |--------------- v MAX_OFFSET + | + - - - - - - - - - - -+ --- (bytes) + | | | !All_INTERIOR_PTRS ^ | + | | | sets j only for hb_sz | + | | Object N | valid object offsets. | | + v | | All objects WORDSZ v v + --- +----------------------+ aligned. --- --- + +DISCARD_WORDS is normally zero. Indeed the collector has not been tested +with another value in ages. diff --git a/gc/base_lib b/gc/base_lib new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/gc/base_lib @@ -0,0 +1 @@ + diff --git a/gc/blacklst.c b/gc/blacklst.c new file mode 100644 index 0000000..0d623c0 --- /dev/null +++ b/gc/blacklst.c @@ -0,0 +1,291 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, August 9, 1995 6:09 pm PDT */ +# include "gc_priv.h" + +/* + * We maintain several hash tables of hblks that have had false hits. + * Each contains one bit per hash bucket; If any page in the bucket + * has had a false hit, we assume that all of them have. + * See the definition of page_hash_table in gc_private.h. + * False hits from the stack(s) are much more dangerous than false hits + * from elsewhere, since the former can pin a large object that spans the + * block, eventhough it does not start on the dangerous block. + */ + +/* + * Externally callable routines are: + + * GC_add_to_black_list_normal + * GC_add_to_black_list_stack + * GC_promote_black_lists + * GC_is_black_listed + * + * All require that the allocator lock is held. + */ + +/* Pointers to individual tables. We replace one table by another by */ +/* switching these pointers. */ +word * GC_old_normal_bl; + /* Nonstack false references seen at last full */ + /* collection. */ +word * GC_incomplete_normal_bl; + /* Nonstack false references seen since last */ + /* full collection. */ +word * GC_old_stack_bl; +word * GC_incomplete_stack_bl; + +word GC_total_stack_black_listed; + +word GC_black_list_spacing = MINHINCR*HBLKSIZE; /* Initial rough guess */ + +void GC_clear_bl(); + +void GC_default_print_heap_obj_proc(p) +ptr_t p; +{ + ptr_t base = GC_base(p); + + GC_err_printf2("start: 0x%lx, appr. length: %ld", base, GC_size(base)); +} + +void (*GC_print_heap_obj)(/* char * s, ptr_t p */) = + GC_default_print_heap_obj_proc; + +void GC_print_source_ptr(p) +ptr_t p; +{ + ptr_t base = GC_base(p); + if (0 == base) { + if (0 == p) { + GC_err_printf0("in register"); + } else { + GC_err_printf0("in root set"); + } + } else { + GC_err_printf0("in object at "); + (*GC_print_heap_obj)(base); + } +} + +void GC_bl_init() +{ +# ifndef ALL_INTERIOR_POINTERS + GC_old_normal_bl = (word *) + GC_scratch_alloc((word)(sizeof (page_hash_table))); + GC_incomplete_normal_bl = (word *)GC_scratch_alloc + ((word)(sizeof(page_hash_table))); + if (GC_old_normal_bl == 0 || GC_incomplete_normal_bl == 0) { + GC_err_printf0("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_normal_bl); + GC_clear_bl(GC_incomplete_normal_bl); +# endif + GC_old_stack_bl = (word *)GC_scratch_alloc((word)(sizeof(page_hash_table))); + GC_incomplete_stack_bl = (word *)GC_scratch_alloc + ((word)(sizeof(page_hash_table))); + if (GC_old_stack_bl == 0 || GC_incomplete_stack_bl == 0) { + GC_err_printf0("Insufficient memory for black list\n"); + EXIT(); + } + GC_clear_bl(GC_old_stack_bl); + GC_clear_bl(GC_incomplete_stack_bl); +} + +void GC_clear_bl(doomed) +word *doomed; +{ + BZERO(doomed, sizeof(page_hash_table)); +} + +void GC_copy_bl(old, new) +word *new, *old; +{ + BCOPY(old, new, sizeof(page_hash_table)); +} + +static word total_stack_black_listed(); + +/* Signal the completion of a collection. Turn the incomplete black */ +/* lists into new black lists, etc. */ +void GC_promote_black_lists() +{ + word * very_old_normal_bl = GC_old_normal_bl; + word * very_old_stack_bl = GC_old_stack_bl; + + GC_old_normal_bl = GC_incomplete_normal_bl; + GC_old_stack_bl = GC_incomplete_stack_bl; +# ifndef ALL_INTERIOR_POINTERS + GC_clear_bl(very_old_normal_bl); +# endif + GC_clear_bl(very_old_stack_bl); + GC_incomplete_normal_bl = very_old_normal_bl; + GC_incomplete_stack_bl = very_old_stack_bl; + GC_total_stack_black_listed = total_stack_black_listed(); +# ifdef PRINTSTATS + GC_printf1("%ld bytes in heap blacklisted for interior pointers\n", + (unsigned long)GC_total_stack_black_listed); +# endif + if (GC_total_stack_black_listed != 0) { + GC_black_list_spacing = + HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed); + } + if (GC_black_list_spacing < 3 * HBLKSIZE) { + GC_black_list_spacing = 3 * HBLKSIZE; + } +} + +void GC_unpromote_black_lists() +{ +# ifndef ALL_INTERIOR_POINTERS + GC_copy_bl(GC_old_normal_bl, GC_incomplete_normal_bl); +# endif + GC_copy_bl(GC_old_stack_bl, GC_incomplete_stack_bl); +} + +# ifndef ALL_INTERIOR_POINTERS +/* P is not a valid pointer reference, but it falls inside */ +/* the plausible heap bounds. */ +/* Add it to the normal incomplete black list if appropriate. */ +#ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_normal(p, source) + ptr_t source; +#else + void GC_add_to_black_list_normal(p) +#endif +word p; +{ + if (!(GC_modws_valid_offsets[p & (sizeof(word)-1)])) return; + { + register int index = PHT_HASH(p); + + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_normal_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_normal_bl, index)) { + GC_err_printf2( + "Black listing (normal) 0x%lx referenced from 0x%lx ", + (unsigned long) p, (unsigned long) source); + GC_print_source_ptr(source); + GC_err_puts("\n"); + } +# endif + set_pht_entry_from_index(GC_incomplete_normal_bl, index); + } /* else this is probably just an interior pointer to an allocated */ + /* object, and isn't worth black listing. */ + } +} +# endif + +/* And the same for false pointers from the stack. */ +#ifdef PRINT_BLACK_LIST + void GC_add_to_black_list_stack(p, source) + ptr_t source; +#else + void GC_add_to_black_list_stack(p) +#endif +word p; +{ + register int index = PHT_HASH(p); + + if (HDR(p) == 0 || get_pht_entry_from_index(GC_old_stack_bl, index)) { +# ifdef PRINT_BLACK_LIST + if (!get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + GC_err_printf2( + "Black listing (stack) 0x%lx referenced from 0x%lx ", + (unsigned long)p, (unsigned long)source); + GC_print_source_ptr(source); + GC_err_puts("\n"); + } +# endif + set_pht_entry_from_index(GC_incomplete_stack_bl, index); + } +} + +/* + * Is the block starting at h of size len bytes black listed? If so, + * return the address of the next plausible r such that (r, len) might not + * be black listed. (R may not actually be in the heap. We guarantee only + * that every smaller value of r after h is also black listed.) + * If (h,len) is not black listed, return 0. + * Knows about the structure of the black list hash tables. + */ +struct hblk * GC_is_black_listed(h, len) +struct hblk * h; +word len; +{ + register int index = PHT_HASH((word)h); + register word i; + word nblocks = divHBLKSZ(len); + +# ifndef ALL_INTERIOR_POINTERS + if (get_pht_entry_from_index(GC_old_normal_bl, index) + || get_pht_entry_from_index(GC_incomplete_normal_bl, index)) { + return(h+1); + } +# endif + + for (i = 0; ; ) { + if (GC_old_stack_bl[divWORDSZ(index)] == 0 + && GC_incomplete_stack_bl[divWORDSZ(index)] == 0) { + /* An easy case */ + i += WORDSZ - modWORDSZ(index); + } else { + if (get_pht_entry_from_index(GC_old_stack_bl, index) + || get_pht_entry_from_index(GC_incomplete_stack_bl, index)) { + return(h+i+1); + } + i++; + } + if (i >= nblocks) break; + index = PHT_HASH((word)(h+i)); + } + return(0); +} + + +/* Return the number of blacklisted blocks in a given range. */ +/* Used only for statistical purposes. */ +/* Looks only at the GC_incomplete_stack_bl. */ +word GC_number_stack_black_listed(start, endp1) +struct hblk *start, *endp1; +{ + register struct hblk * h; + word result = 0; + + for (h = start; h < endp1; h++) { + register int index = PHT_HASH((word)h); + + if (get_pht_entry_from_index(GC_old_stack_bl, index)) result++; + } + return(result); +} + + +/* Return the total number of (stack) black-listed bytes. */ +static word total_stack_black_listed() +{ + register unsigned i; + word total = 0; + + for (i = 0; i < GC_n_heap_sects; i++) { + struct hblk * start = (struct hblk *) GC_heap_sects[i].hs_start; + word len = (word) GC_heap_sects[i].hs_bytes; + struct hblk * endp1 = start + len/HBLKSIZE; + + total += GC_number_stack_black_listed(start, endp1); + } + return(total * HBLKSIZE); +} + diff --git a/gc/callprocs b/gc/callprocs new file mode 100755 index 0000000..a8793f0 --- /dev/null +++ b/gc/callprocs @@ -0,0 +1,4 @@ +#!/bin/sh +GC_DEBUG=1 +export GC_DEBUG +$* 2>&1 | awk '{print "0x3e=c\""$0"\""};/^\t##PC##=/ {if ($2 != 0) {print $2"?i"}}' | adb $1 | sed "s/^ >/>/" diff --git a/gc/checksums.c b/gc/checksums.c new file mode 100644 index 0000000..212655f --- /dev/null +++ b/gc/checksums.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1992-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, March 29, 1995 12:51 pm PST */ +# ifdef CHECKSUMS + +# include "gc_priv.h" + +/* This is debugging code intended to verify the results of dirty bit */ +/* computations. Works only in a single threaded environment. */ +/* We assume that stubborn objects are changed only when they are */ +/* enabled for writing. (Certain kinds of writing are actually */ +/* safe under other conditions.) */ +# define NSUMS 2000 + +# define OFFSET 0x10000 + +typedef struct { + GC_bool new_valid; + word old_sum; + word new_sum; + struct hblk * block; /* Block to which this refers + OFFSET */ + /* to hide it from colector. */ +} page_entry; + +page_entry GC_sums [NSUMS]; + +word GC_checksum(h) +struct hblk *h; +{ + register word *p = (word *)h; + register word *lim = (word *)(h+1); + register word result = 0; + + while (p < lim) { + result += *p++; + } + return(result | 0x80000000 /* doesn't look like pointer */); +} + +# ifdef STUBBORN_ALLOC +/* Check whether a stubborn object from the given block appears on */ +/* the appropriate free list. */ +GC_bool GC_on_free_list(h) +struct hblk *h; +{ + register hdr * hhdr = HDR(h); + register int sz = hhdr -> hb_sz; + ptr_t p; + + if (sz > MAXOBJSZ) return(FALSE); + for (p = GC_sobjfreelist[sz]; p != 0; p = obj_link(p)) { + if (HBLKPTR(p) == h) return(TRUE); + } + return(FALSE); +} +# endif + +int GC_n_dirty_errors; +int GC_n_changed_errors; +int GC_n_clean; +int GC_n_dirty; + +void GC_update_check_page(h, index) +struct hblk *h; +int index; +{ + page_entry *pe = GC_sums + index; + register hdr * hhdr = HDR(h); + + if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed"); + pe -> old_sum = pe -> new_sum; + pe -> new_sum = GC_checksum(h); +# ifndef MSWIN32 + if (pe -> new_sum != 0 && !GC_page_was_ever_dirty(h)) { + GC_printf1("GC_page_was_ever_dirty(0x%lx) is wrong\n", + (unsigned long)h); + } +# endif + if (GC_page_was_dirty(h)) { + GC_n_dirty++; + } else { + GC_n_clean++; + } + if (pe -> new_valid && pe -> old_sum != pe -> new_sum) { + if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) { + /* Set breakpoint here */GC_n_dirty_errors++; + } +# ifdef STUBBORN_ALLOC + if (!IS_FORWARDING_ADDR_OR_NIL(hhdr) + && hhdr -> hb_map != GC_invalid_map + && hhdr -> hb_obj_kind == STUBBORN + && !GC_page_was_changed(h) + && !GC_on_free_list(h)) { + /* if GC_on_free_list(h) then reclaim may have touched it */ + /* without any allocations taking place. */ + /* Set breakpoint here */GC_n_changed_errors++; + } +# endif + } + pe -> new_valid = TRUE; + pe -> block = h + OFFSET; +} + +word GC_bytes_in_used_blocks; + +void GC_add_block(h, dummy) +struct hblk *h; +word dummy; +{ + register hdr * hhdr = HDR(h); + register bytes = WORDS_TO_BYTES(hhdr -> hb_sz); + + bytes += HDR_BYTES + HBLKSIZE-1; + bytes &= ~(HBLKSIZE-1); + GC_bytes_in_used_blocks += bytes; +} + +void GC_check_blocks() +{ + word bytes_in_free_blocks = 0; + struct hblk * h = GC_hblkfreelist; + hdr * hhdr = HDR(h); + word sz; + + GC_bytes_in_used_blocks = 0; + GC_apply_to_all_blocks(GC_add_block, (word)0); + while (h != 0) { + sz = hhdr -> hb_sz; + bytes_in_free_blocks += sz; + h = hhdr -> hb_next; + hhdr = HDR(h); + } + GC_printf2("GC_bytes_in_used_blocks = %ld, bytes_in_free_blocks = %ld ", + GC_bytes_in_used_blocks, bytes_in_free_blocks); + GC_printf1("GC_heapsize = %ld\n", GC_heapsize); + if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) { + GC_printf0("LOST SOME BLOCKS!!\n"); + } +} + +/* Should be called immediately after GC_read_dirty and GC_read_changed. */ +void GC_check_dirty() +{ + register int index; + register unsigned i; + register struct hblk *h; + register ptr_t start; + + GC_check_blocks(); + + GC_n_dirty_errors = 0; + GC_n_changed_errors = 0; + GC_n_clean = 0; + GC_n_dirty = 0; + + index = 0; + for (i = 0; i < GC_n_heap_sects; i++) { + start = GC_heap_sects[i].hs_start; + for (h = (struct hblk *)start; + h < (struct hblk *)(start + GC_heap_sects[i].hs_bytes); + h++) { + GC_update_check_page(h, index); + index++; + if (index >= NSUMS) goto out; + } + } +out: + GC_printf2("Checked %lu clean and %lu dirty pages\n", + (unsigned long) GC_n_clean, (unsigned long) GC_n_dirty); + if (GC_n_dirty_errors > 0) { + GC_printf1("Found %lu dirty bit errors\n", + (unsigned long)GC_n_dirty_errors); + } + if (GC_n_changed_errors > 0) { + GC_printf1("Found %lu changed bit errors\n", + (unsigned long)GC_n_changed_errors); + GC_printf0("These may be benign (provoked by nonpointer changes)\n"); +# ifdef THREADS + GC_printf0( + "Also expect 1 per thread currently allocating a stubborn obj.\n"); +# endif + } +} + +# else + +extern int GC_quiet; + /* ANSI C doesn't allow translation units to be empty. */ + /* So we guarantee this one is nonempty. */ + +# endif /* CHECKSUMS */ diff --git a/gc/cord/README b/gc/cord/README new file mode 100644 index 0000000..6210145 --- /dev/null +++ b/gc/cord/README @@ -0,0 +1,31 @@ +Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +Please send bug reports to Hans-J. Boehm (boehm@sgi.com). + +This is a string packages that uses a tree-based representation. +See cord.h for a description of the functions provided. Ec.h describes +"extensible cords", which are essentially output streams that write +to a cord. These allow for efficient construction of cords without +requiring a bound on the size of a cord. + +de.c is a very dumb text editor that illustrates the use of cords. +It maintains a list of file versions. Each version is simply a +cord representing the file contents. Nonetheless, standard +editing operations are efficient, even on very large files. +(Its 3 line "user manual" can be obtained by invoking it without +arguments. Note that ^R^N and ^R^P move the cursor by +almost a screen. It does not understand tabs, which will show +up as highlighred "I"s. Use the UNIX "expand" program first.) +To build the editor, type "make cord/de" in the gc directory. + +This package assumes an ANSI C compiler such as gcc. It will +not compile with an old-style K&R compiler. diff --git a/gc/cord/SCOPTIONS.amiga b/gc/cord/SCOPTIONS.amiga new file mode 100755 index 0000000..2a09197 --- /dev/null +++ b/gc/cord/SCOPTIONS.amiga @@ -0,0 +1,14 @@ +MATH=STANDARD +CPU=68030 +NOSTACKCHECK +OPTIMIZE +VERBOSE +NOVERSION +NOICONS +OPTIMIZERTIME +INCLUDEDIR=/ +DEFINE AMIGA +LIBRARY=cord.lib +LIBRARY=/gc.lib +IGNORE=100 +IGNORE=161 diff --git a/gc/cord/SMakefile.amiga b/gc/cord/SMakefile.amiga new file mode 100644 index 0000000..5aef131 --- /dev/null +++ b/gc/cord/SMakefile.amiga @@ -0,0 +1,20 @@ +# Makefile for cord.lib +# Michel Schinz 1994/07/20 + +OBJS = cordbscs.o cordprnt.o cordxtra.o + +all: cord.lib cordtest + +cordbscs.o: cordbscs.c +cordprnt.o: cordprnt.c +cordxtra.o: cordxtra.c +cordtest.o: cordtest.c + +cord.lib: $(OBJS) + oml cord.lib r $(OBJS) + +cordtest: cordtest.o cord.lib + sc cordtest.o link + +clean: + delete cord.lib cordtest \#?.o \#?.lnk diff --git a/gc/cord/cord.h b/gc/cord/cord.h new file mode 100644 index 0000000..584112f --- /dev/null +++ b/gc/cord/cord.h @@ -0,0 +1,327 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* Boehm, October 5, 1995 4:20 pm PDT */ + +/* + * Cords are immutable character strings. A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments. (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords. They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + * concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + * performs editing operations by producing a new cord representing + * the file after echa character change (and keeping the old ones in an + * edit history) + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord. + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type fo a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i; CORD_FOR(i, cord) { ... CORD_pos_fetch(i) ... } - + * examine each character in a cord. CORD_pos_fetch(i) is the char. + * CORD_fetch(int i) - Retrieve i'th character (slowly). + * CORD_cmp(cord1, cord2) - compare two cords. + * CORD_from_file(FILE * f) - turn a read-only file into a cord. + * CORD_to_char_star(cord) - convert to C string. + * (Non-NULL C constant strings are cords.) + * CORD_printf (etc.) - cord version of printf. Use %r for cords. + */ +# ifndef CORD_H + +# define CORD_H +# include <stddef.h> +# include <stdio.h> +/* Cords have type const char *. This is cheating quite a bit, and not */ +/* 100% portable. But it means that nonempty character string */ +/* constants may be used as cords directly, provided the string is */ +/* never modified in place. The empty cord is represented by, and */ +/* can be written as, 0. */ + +typedef const char * CORD; + +/* An empty cord is always represented as nil */ +# define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords. If the arguments are C strings, they may */ +/* not be subsequently altered. */ +CORD CORD_cat(CORD x, CORD y); + +/* Concatenate a cord and a C string with known length. Except for the */ +/* empty string case, this is a special case of CORD_cat. Since the */ +/* length is known, it can be faster. */ +/* The string y is shared with the resulting CORD. Hence it should */ +/* not be altered by the caller. */ +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny); + +/* Compute the length of a cord */ +size_t CORD_len(CORD x); + +/* Cords may be represented by functions defining the ith character */ +typedef char (* CORD_fn)(size_t i, void * client_data); + +/* Turn a functional description into a cord. */ +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len); + +/* Return the substring (subcord really) of x with length at most n, */ +/* starting at position i. (The initial character has position 0.) */ +CORD CORD_substr(CORD x, size_t i, size_t n); + +/* Return the argument, but rebalanced to allow more efficient */ +/* character retrieval, substring operations, and comparisons. */ +/* This is useful only for cords that were built using repeated */ +/* concatenation. Guarantees log time access to the result, unless */ +/* x was obtained through a large number of repeated substring ops */ +/* or the embedded functional descriptions take longer to evaluate. */ +/* May reallocate significant parts of the cord. The argument is not */ +/* modified; only the result is balanced. */ +CORD CORD_balance(CORD x); + +/* The following traverse a cord by applying a function to each */ +/* character. This is occasionally appropriate, especially where */ +/* speed is crucial. But, since C doesn't have nested functions, */ +/* clients of this sort of traversal are clumsy to write. Consider */ +/* the functions that operate on cord positions instead. */ + +/* Function to iteratively apply to individual characters in cord. */ +typedef int (* CORD_iter_fn)(char c, void * client_data); + +/* Function to apply to substrings of a cord. Each substring is a */ +/* a C character string, not a general cord. */ +typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data); +# define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order, */ +/* starting at position i. If */ +/* f2 is not CORD_NO_FN, then multiple calls to f1 may be replaced by */ +/* a single call to f2. The parameter f2 is provided only to allow */ +/* some optimization by the client. This terminates when the right */ +/* end of this string is reached, or when f1 or f2 return != 0. In the */ +/* latter case CORD_iter returns != 0. Otherwise it returns 0. */ +/* The specified value of i must be < CORD_len(x). */ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data); + +/* A simpler version that starts at 0, and without f2: */ +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data); +# define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning. No provisions for */ +/* CORD_batched_iter_fn. */ +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data); + +/* A simpler version that starts at the end: */ +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data); + +/* Functions that operate on cord positions. The easy way to traverse */ +/* cords. A cord position is logically a pair consisting of a cord */ +/* and an index into that cord. But it is much faster to retrieve a */ +/* charcter based on a position than on an index. Unfortunately, */ +/* positions are big (order of a few 100 bytes), so allocate them with */ +/* caution. */ +/* Things in cord_pos.h should be treated as opaque, except as */ +/* described below. Also note that */ +/* CORD_pos_fetch, CORD_next and CORD_prev have both macro and function */ +/* definitions. The former may evaluate their argument more than once. */ +# include "private/cord_pos.h" + +/* + Visible definitions from above: + + typedef <OPAQUE but fairly big> CORD_pos[1]; + + * Extract the cord from a position: + CORD CORD_pos_to_cord(CORD_pos p); + + * Extract the current index from a position: + size_t CORD_pos_to_index(CORD_pos p); + + * Fetch the character located at the given position: + char CORD_pos_fetch(CORD_pos p); + + * Initialize the position to refer to the given cord and index. + * Note that this is the most expensive function on positions: + void CORD_set_pos(CORD_pos p, CORD x, size_t i); + + * Advance the position to the next character. + * P must be initialized and valid. + * Invalidates p if past end: + void CORD_next(CORD_pos p); + + * Move the position to the preceding character. + * P must be initialized and valid. + * Invalidates p if past beginning: + void CORD_prev(CORD_pos p); + + * Is the position valid, i.e. inside the cord? + int CORD_pos_valid(CORD_pos p); +*/ +# define CORD_FOR(pos, cord) \ + for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + + +/* An out of memory handler to call. May be supplied by client. */ +/* Must not return. */ +extern void (* CORD_oom_fn)(void); + +/* Dump the representation of x to stdout in an implementation defined */ +/* manner. Intended for debugging only. */ +void CORD_dump(CORD x); + +/* The following could easily be implemented by the client. They are */ +/* provided in cordxtra.c for convenience. */ + +/* Concatenate a character to the end of a cord. */ +CORD CORD_cat_char(CORD x, char c); + +/* Concatenate n cords. */ +CORD CORD_catn(int n, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1) */ +char CORD_fetch(CORD x, size_t i); + +/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y */ +int CORD_cmp(CORD x, CORD y); + +/* A generalization that takes both starting positions for the */ +/* comparison, and a limit on the number of characters to be compared. */ +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len); + +/* Find the first occurrence of s in x at position start or later. */ +/* Return the position of the first character of s in x, or */ +/* CORD_NOT_FOUND if there is none. */ +size_t CORD_str(CORD x, size_t start, CORD s); + +/* Return a cord consisting of i copies of (possibly NUL) c. Dangerous */ +/* in conjunction with CORD_to_char_star. */ +/* The resulting representation takes constant space, independent of i. */ +CORD CORD_chars(char c, size_t i); +# define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord. The file must be seekable. Its contents */ +/* must remain constant. The file may be accessed as an immediate */ +/* result of this call and/or as a result of subsequent accesses to */ +/* the cord. Short files are likely to be immediately read, but */ +/* long files are likely to be read on demand, possibly relying on */ +/* stdio for buffering. */ +/* We must have exclusive access to the descriptor f, i.e. we may */ +/* read it at any time, and expect the file pointer to be */ +/* where we left it. Normally this should be invoked as */ +/* CORD_from_file(fopen(...)) */ +/* CORD_from_file arranges to close the file descriptor when it is no */ +/* longer needed (e.g. when the result becomes inaccessible). */ +/* The file f must be such that ftell reflects the actual character */ +/* position in the file, i.e. the number of characters that can be */ +/* or were read with fread. On UNIX systems this is always true. On */ +/* MS Windows systems, f must be opened in binary mode. */ +CORD CORD_from_file(FILE * f); + +/* Equivalent to the above, except that the entire file will be read */ +/* and the file pointer will be closed immediately. */ +/* The binary mode restriction from above does not apply. */ +CORD CORD_from_file_eager(FILE * f); + +/* Equivalent to the above, except that the file will be read on demand.*/ +/* The binary mode restriction applies. */ +CORD CORD_from_file_lazy(FILE * f); + +/* Turn a cord into a C string. The result shares no structure with */ +/* x, and is thus modifiable. */ +char * CORD_to_char_star(CORD x); + +/* Turn a C string into a CORD. The C string is copied, and so may */ +/* subsequently be modified. */ +CORD CORD_from_char_star(const char *s); + +/* Identical to the above, but the result may share structure with */ +/* the argument and is thus not modifiable. */ +const char * CORD_to_const_char_star(CORD x); + +/* Write a cord to a file, starting at the current position. No */ +/* trailing NULs are newlines are added. */ +/* Returns EOF if a write error occurs, 1 otherwise. */ +int CORD_put(CORD x, FILE * f); + +/* "Not found" result for the following two functions. */ +# define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr. Returns the position (an integer, not */ +/* a pointer) of the first occurrence of (char) c inside x at position */ +/* i or later. The value i must be < CORD_len(x). */ +size_t CORD_chr(CORD x, size_t i, int c); + +/* A vague analog of strrchr. Returns index of the last occurrence */ +/* of (char) c inside x at position i or earlier. The value i */ +/* must be < CORD_len(x). */ +size_t CORD_rchr(CORD x, size_t i, int c); + + +/* The following are also not primitive, but are implemented in */ +/* cordprnt.c. They provide functionality similar to the ANSI C */ +/* functions with corresponding names, but with the following */ +/* additions and changes: */ +/* 1. A %r conversion specification specifies a CORD argument. Field */ +/* width, precision, etc. have the same semantics as for %s. */ +/* (Note that %c,%C, and %S were already taken.) */ +/* 2. The format string is represented as a CORD. */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st */ /* argument. Unlike their ANSI C versions, there is no need to guess */ +/* the correct buffer size. */ +/* 4. Most of the conversions are implement through the native */ +/* vsprintf. Hence they are usually no faster, and */ +/* idiosyncracies of the native printf are preserved. However, */ +/* CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied; */ +/* the result shares the original structure. This may make them */ +/* very efficient in some unusual applications. */ +/* The format string is copied. */ +/* All functions return the number of characters generated or -1 on */ +/* error. This complies with the ANSI standard, but is inconsistent */ +/* with some older implementations of sprintf. */ + +/* The implementation of these is probably less portable than the rest */ +/* of this package. */ + +#ifndef CORD_NO_IO + +#include <stdarg.h> + +int CORD_sprintf(CORD * out, CORD format, ...); +int CORD_vsprintf(CORD * out, CORD format, va_list args); +int CORD_fprintf(FILE * f, CORD format, ...); +int CORD_vfprintf(FILE * f, CORD format, va_list args); +int CORD_printf(CORD format, ...); +int CORD_vprintf(CORD format, va_list args); + +#endif /* CORD_NO_IO */ + +# endif /* CORD_H */ diff --git a/gc/cord/cordbscs.c b/gc/cord/cordbscs.c new file mode 100644 index 0000000..9fc894d --- /dev/null +++ b/gc/cord/cordbscs.c @@ -0,0 +1,915 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* Boehm, October 3, 1994 5:19 pm PDT */ +# include "gc.h" +# include "cord.h" +# include <stdlib.h> +# include <stdio.h> +# include <string.h> + +/* An implementation of the cord primitives. These are the only */ +/* Functions that understand the representation. We perform only */ +/* minimal checks on arguments to these functions. Out of bounds */ +/* arguments to the iteration functions may result in client functions */ +/* invoked on garbage data. In most cases, client functions should be */ +/* programmed defensively enough that this does not result in memory */ +/* smashes. */ + +typedef void (* oom_fn)(void); + +oom_fn CORD_oom_fn = (oom_fn) 0; + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory\n"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +typedef unsigned long word; + +typedef union { + struct Concatenation { + char null; + char header; + char depth; /* concatenation nesting depth. */ + unsigned char left_len; + /* Length of left child if it is sufficiently */ + /* short; 0 otherwise. */ +# define MAX_LEFT_LEN 255 + word len; + CORD left; /* length(left) > 0 */ + CORD right; /* length(right) > 0 */ + } concatenation; + struct Function { + char null; + char header; + char depth; /* always 0 */ + char left_len; /* always 0 */ + word len; + CORD_fn fn; + void * client_data; + } function; + struct Generic { + char null; + char header; + char depth; + char left_len; + word len; + } generic; + char string[1]; +} CordRep; + +# define CONCAT_HDR 1 + +# define FN_HDR 4 +# define SUBSTR_HDR 6 + /* Substring nodes are a special case of function nodes. */ + /* The client_data field is known to point to a substr_args */ + /* structure, and the function is either CORD_apply_access_fn */ + /* or CORD_index_access_fn. */ + +/* The following may be applied only to function and concatenation nodes: */ +#define IS_CONCATENATION(s) (((CordRep *)s)->generic.header == CONCAT_HDR) + +#define IS_FUNCTION(s) ((((CordRep *)s)->generic.header & FN_HDR) != 0) + +#define IS_SUBSTR(s) (((CordRep *)s)->generic.header == SUBSTR_HDR) + +#define LEN(s) (((CordRep *)s) -> generic.len) +#define DEPTH(s) (((CordRep *)s) -> generic.depth) +#define GEN_LEN(s) (CORD_IS_STRING(s) ? strlen(s) : LEN(s)) + +#define LEFT_LEN(c) ((c) -> left_len != 0? \ + (c) -> left_len \ + : (CORD_IS_STRING((c) -> left) ? \ + (c) -> len - GEN_LEN((c) -> right) \ + : LEN((c) -> left))) + +#define SHORT_LIMIT (sizeof(CordRep) - 1) + /* Cords shorter than this are C strings */ + + +/* Dump the internal representation of x to stdout, with initial */ +/* indentation level n. */ +void CORD_dump_inner(CORD x, unsigned n) +{ + register size_t i; + + for (i = 0; i < (size_t)n; i++) { + fputs(" ", stdout); + } + if (x == 0) { + fputs("NIL\n", stdout); + } else if (CORD_IS_STRING(x)) { + for (i = 0; i <= SHORT_LIMIT; i++) { + if (x[i] == '\0') break; + putchar(x[i]); + } + if (x[i] != '\0') fputs("...", stdout); + putchar('\n'); + } else if (IS_CONCATENATION(x)) { + register struct Concatenation * conc = + &(((CordRep *)x) -> concatenation); + printf("Concatenation: %p (len: %d, depth: %d)\n", + x, (int)(conc -> len), (int)(conc -> depth)); + CORD_dump_inner(conc -> left, n+1); + CORD_dump_inner(conc -> right, n+1); + } else /* function */{ + register struct Function * func = + &(((CordRep *)x) -> function); + if (IS_SUBSTR(x)) printf("(Substring) "); + printf("Function: %p (len: %d): ", x, (int)(func -> len)); + for (i = 0; i < 20 && i < func -> len; i++) { + putchar((*(func -> fn))(i, func -> client_data)); + } + if (i < func -> len) fputs("...", stdout); + putchar('\n'); + } +} + +/* Dump the internal representation of x to stdout */ +void CORD_dump(CORD x) +{ + CORD_dump_inner(x, 0); + fflush(stdout); +} + +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny) +{ + register size_t result_len; + register size_t lenx; + register int depth; + + if (x == CORD_EMPTY) return(y); + if (leny == 0) return(x); + if (CORD_IS_STRING(x)) { + lenx = strlen(x); + result_len = lenx + leny; + if (result_len <= SHORT_LIMIT) { + register char * result = GC_MALLOC_ATOMIC(result_len+1); + + if (result == 0) OUT_OF_MEMORY; + memcpy(result, x, lenx); + memcpy(result + lenx, y, leny); + result[result_len] = '\0'; + return((CORD) result); + } else { + depth = 1; + } + } else { + register CORD right; + register CORD left; + register char * new_right; + register size_t right_len; + + lenx = LEN(x); + + if (leny <= SHORT_LIMIT/2 + && IS_CONCATENATION(x) + && CORD_IS_STRING(right = ((CordRep *)x) -> concatenation.right)) { + /* Merge y into right part of x. */ + if (!CORD_IS_STRING(left = ((CordRep *)x) -> concatenation.left)) { + right_len = lenx - LEN(left); + } else if (((CordRep *)x) -> concatenation.left_len != 0) { + right_len = lenx - ((CordRep *)x) -> concatenation.left_len; + } else { + right_len = strlen(right); + } + result_len = right_len + leny; /* length of new_right */ + if (result_len <= SHORT_LIMIT) { + new_right = GC_MALLOC_ATOMIC(result_len + 1); + memcpy(new_right, right, right_len); + memcpy(new_right + right_len, y, leny); + new_right[result_len] = '\0'; + y = new_right; + leny = result_len; + x = left; + lenx -= right_len; + /* Now fall through to concatenate the two pieces: */ + } + if (CORD_IS_STRING(x)) { + depth = 1; + } else { + depth = DEPTH(x) + 1; + } + } else { + depth = DEPTH(x) + 1; + } + result_len = lenx + leny; + } + { + /* The general case; lenx, result_len is known: */ + register struct Concatenation * result; + + result = GC_NEW(struct Concatenation); + if (result == 0) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = depth; + if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; + result->len = result_len; + result->left = x; + result->right = y; + if (depth > MAX_DEPTH) { + return(CORD_balance((CORD)result)); + } else { + return((CORD) result); + } + } +} + + +CORD CORD_cat(CORD x, CORD y) +{ + register size_t result_len; + register int depth; + register size_t lenx; + + if (x == CORD_EMPTY) return(y); + if (y == CORD_EMPTY) return(x); + if (CORD_IS_STRING(y)) { + return(CORD_cat_char_star(x, y, strlen(y))); + } else if (CORD_IS_STRING(x)) { + lenx = strlen(x); + depth = DEPTH(y) + 1; + } else { + register int depthy = DEPTH(y); + + lenx = LEN(x); + depth = DEPTH(x) + 1; + if (depthy >= depth) depth = depthy + 1; + } + result_len = lenx + LEN(y); + { + register struct Concatenation * result; + + result = GC_NEW(struct Concatenation); + if (result == 0) OUT_OF_MEMORY; + result->header = CONCAT_HDR; + result->depth = depth; + if (lenx <= MAX_LEFT_LEN) result->left_len = lenx; + result->len = result_len; + result->left = x; + result->right = y; + return((CORD) result); + } +} + + + +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len) +{ + if (len <= 0) return(0); + if (len <= SHORT_LIMIT) { + register char * result; + register size_t i; + char buf[SHORT_LIMIT+1]; + register char c; + + for (i = 0; i < len; i++) { + c = (*fn)(i, client_data); + if (c == '\0') goto gen_case; + buf[i] = c; + } + buf[i] = '\0'; + result = GC_MALLOC_ATOMIC(len+1); + if (result == 0) OUT_OF_MEMORY; + strcpy(result, buf); + result[len] = '\0'; + return((CORD) result); + } + gen_case: + { + register struct Function * result; + + result = GC_NEW(struct Function); + if (result == 0) OUT_OF_MEMORY; + result->header = FN_HDR; + /* depth is already 0 */ + result->len = len; + result->fn = fn; + result->client_data = client_data; + return((CORD) result); + } +} + +size_t CORD_len(CORD x) +{ + if (x == 0) { + return(0); + } else { + return(GEN_LEN(x)); + } +} + +struct substr_args { + CordRep * sa_cord; + size_t sa_index; +}; + +char CORD_index_access_fn(size_t i, void * client_data) +{ + register struct substr_args *descr = (struct substr_args *)client_data; + + return(((char *)(descr->sa_cord))[i + descr->sa_index]); +} + +char CORD_apply_access_fn(size_t i, void * client_data) +{ + register struct substr_args *descr = (struct substr_args *)client_data; + register struct Function * fn_cord = &(descr->sa_cord->function); + + return((*(fn_cord->fn))(i + descr->sa_index, fn_cord->client_data)); +} + +/* A version of CORD_substr that simply returns a function node, thus */ +/* postponing its work. The fourth argument is a function that may */ +/* be used for efficient access to the ith character. */ +/* Assumes i >= 0 and i + n < length(x). */ +CORD CORD_substr_closure(CORD x, size_t i, size_t n, CORD_fn f) +{ + register struct substr_args * sa = GC_NEW(struct substr_args); + CORD result; + + if (sa == 0) OUT_OF_MEMORY; + sa->sa_cord = (CordRep *)x; + sa->sa_index = i; + result = CORD_from_fn(f, (void *)sa, n); + ((CordRep *)result) -> function.header = SUBSTR_HDR; + return (result); +} + +# define SUBSTR_LIMIT (10 * SHORT_LIMIT) + /* Substrings of function nodes and flat strings shorter than */ + /* this are flat strings. Othewise we use a functional */ + /* representation, which is significantly slower to access. */ + +/* A version of CORD_substr that assumes i >= 0, n > 0, and i + n < length(x).*/ +CORD CORD_substr_checked(CORD x, size_t i, size_t n) +{ + if (CORD_IS_STRING(x)) { + if (n > SUBSTR_LIMIT) { + return(CORD_substr_closure(x, i, n, CORD_index_access_fn)); + } else { + register char * result = GC_MALLOC_ATOMIC(n+1); + + if (result == 0) OUT_OF_MEMORY; + strncpy(result, x+i, n); + result[n] = '\0'; + return(result); + } + } else if (IS_CONCATENATION(x)) { + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + register size_t left_len; + register size_t right_len; + + left_len = LEFT_LEN(conc); + right_len = conc -> len - left_len; + if (i >= left_len) { + if (n == right_len) return(conc -> right); + return(CORD_substr_checked(conc -> right, i - left_len, n)); + } else if (i+n <= left_len) { + if (n == left_len) return(conc -> left); + return(CORD_substr_checked(conc -> left, i, n)); + } else { + /* Need at least one character from each side. */ + register CORD left_part; + register CORD right_part; + register size_t left_part_len = left_len - i; + + if (i == 0) { + left_part = conc -> left; + } else { + left_part = CORD_substr_checked(conc -> left, i, left_part_len); + } + if (i + n == right_len + left_len) { + right_part = conc -> right; + } else { + right_part = CORD_substr_checked(conc -> right, 0, + n - left_part_len); + } + return(CORD_cat(left_part, right_part)); + } + } else /* function */ { + if (n > SUBSTR_LIMIT) { + if (IS_SUBSTR(x)) { + /* Avoid nesting substring nodes. */ + register struct Function * f = &(((CordRep *)x) -> function); + register struct substr_args *descr = + (struct substr_args *)(f -> client_data); + + return(CORD_substr_closure((CORD)descr->sa_cord, + i + descr->sa_index, + n, f -> fn)); + } else { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + } else { + char * result; + register struct Function * f = &(((CordRep *)x) -> function); + char buf[SUBSTR_LIMIT+1]; + register char * p = buf; + register char c; + register int j; + register int lim = i + n; + + for (j = i; j < lim; j++) { + c = (*(f -> fn))(j, f -> client_data); + if (c == '\0') { + return(CORD_substr_closure(x, i, n, CORD_apply_access_fn)); + } + *p++ = c; + } + *p = '\0'; + result = GC_MALLOC_ATOMIC(n+1); + if (result == 0) OUT_OF_MEMORY; + strcpy(result, buf); + return(result); + } + } +} + +CORD CORD_substr(CORD x, size_t i, size_t n) +{ + register size_t len = CORD_len(x); + + if (i >= len || n <= 0) return(0); + /* n < 0 is impossible in a correct C implementation, but */ + /* quite possible under SunOS 4.X. */ + if (i + n > len) n = len - i; +# ifndef __STDC__ + if (i < 0) ABORT("CORD_substr: second arg. negative"); + /* Possible only if both client and C implementation are buggy. */ + /* But empirically this happens frequently. */ +# endif + return(CORD_substr_checked(x, i, n)); +} + +/* See cord.h for definition. We assume i is in range. */ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + register const char *p = x+i; + + if (*p == '\0') ABORT("2nd arg to CORD_iter5 too big"); + if (f2 != CORD_NO_FN) { + return((*f2)(p, client_data)); + } else { + while (*p) { + if ((*f1)(*p, client_data)) return(1); + p++; + } + return(0); + } + } else if (IS_CONCATENATION(x)) { + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + + + if (i > 0) { + register size_t left_len = LEFT_LEN(conc); + + if (i >= left_len) { + return(CORD_iter5(conc -> right, i - left_len, f1, f2, + client_data)); + } + } + if (CORD_iter5(conc -> left, i, f1, f2, client_data)) { + return(1); + } + return(CORD_iter5(conc -> right, 0, f1, f2, client_data)); + } else /* function */ { + register struct Function * f = &(((CordRep *)x) -> function); + register size_t j; + register size_t lim = f -> len; + + for (j = i; j < lim; j++) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + } + return(0); + } +} + +#undef CORD_iter +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data) +{ + return(CORD_iter5(x, 0, f1, CORD_NO_FN, client_data)); +} + +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data) +{ + if (x == 0) return(0); + if (CORD_IS_STRING(x)) { + register const char *p = x + i; + register char c; + + for(;;) { + c = *p; + if (c == '\0') ABORT("2nd arg to CORD_riter4 too big"); + if ((*f1)(c, client_data)) return(1); + if (p == x) break; + p--; + } + return(0); + } else if (IS_CONCATENATION(x)) { + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + register CORD left_part = conc -> left; + register size_t left_len; + + left_len = LEFT_LEN(conc); + if (i >= left_len) { + if (CORD_riter4(conc -> right, i - left_len, f1, client_data)) { + return(1); + } + return(CORD_riter4(left_part, left_len - 1, f1, client_data)); + } else { + return(CORD_riter4(left_part, i, f1, client_data)); + } + } else /* function */ { + register struct Function * f = &(((CordRep *)x) -> function); + register size_t j; + + for (j = i; ; j--) { + if ((*f1)((*(f -> fn))(j, f -> client_data), client_data)) { + return(1); + } + if (j == 0) return(0); + } + } +} + +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data) +{ + return(CORD_riter4(x, CORD_len(x) - 1, f1, client_data)); +} + +/* + * The following functions are concerned with balancing cords. + * Strategy: + * Scan the cord from left to right, keeping the cord scanned so far + * as a forest of balanced trees of exponentialy decreasing length. + * When a new subtree needs to be added to the forest, we concatenate all + * shorter ones to the new tree in the appropriate order, and then insert + * the result into the forest. + * Crucial invariants: + * 1. The concatenation of the forest (in decreasing order) with the + * unscanned part of the rope is equal to the rope being balanced. + * 2. All trees in the forest are balanced. + * 3. forest[i] has depth at most i. + */ + +typedef struct { + CORD c; + size_t len; /* Actual length of c */ +} ForestElement; + +static size_t min_len [ MAX_DEPTH ]; + +static int min_len_init = 0; + +int CORD_max_len; + +typedef ForestElement Forest [ MAX_DEPTH ]; + /* forest[i].len >= fib(i+1) */ + /* The string is the concatenation */ + /* of the forest in order of DECREASING */ + /* indices. */ + +void CORD_init_min_len() +{ + register int i; + register size_t last, previous, current; + + min_len[0] = previous = 1; + min_len[1] = last = 2; + for (i = 2; i < MAX_DEPTH; i++) { + current = last + previous; + if (current < last) /* overflow */ current = last; + min_len[i] = current; + previous = last; + last = current; + } + CORD_max_len = last - 1; + min_len_init = 1; +} + + +void CORD_init_forest(ForestElement * forest, size_t max_len) +{ + register int i; + + for (i = 0; i < MAX_DEPTH; i++) { + forest[i].c = 0; + if (min_len[i] > max_len) return; + } + ABORT("Cord too long"); +} + +/* Add a leaf to the appropriate level in the forest, cleaning */ +/* out lower levels as necessary. */ +/* Also works if x is a balanced tree of concatenations; however */ +/* in this case an extra concatenation node may be inserted above x; */ +/* This node should not be counted in the statement of the invariants. */ +void CORD_add_forest(ForestElement * forest, CORD x, size_t len) +{ + register int i = 0; + register CORD sum = CORD_EMPTY; + register size_t sum_len = 0; + + while (len > min_len[i + 1]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + forest[i].c = 0; + } + i++; + } + /* Sum has depth at most 1 greter than what would be required */ + /* for balance. */ + sum = CORD_cat(sum, x); + sum_len += len; + /* If x was a leaf, then sum is now balanced. To see this */ + /* consider the two cases in which forest[i-1] either is or is */ + /* not empty. */ + while (sum_len >= min_len[i]) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + /* This is again balanced, since sum was balanced, and has */ + /* allowable depth that differs from i by at most 1. */ + forest[i].c = 0; + } + i++; + } + i--; + forest[i].c = sum; + forest[i].len = sum_len; +} + +CORD CORD_concat_forest(ForestElement * forest, size_t expected_len) +{ + register int i = 0; + CORD sum = 0; + size_t sum_len = 0; + + while (sum_len != expected_len) { + if (forest[i].c != 0) { + sum = CORD_cat(forest[i].c, sum); + sum_len += forest[i].len; + } + i++; + } + return(sum); +} + +/* Insert the frontier of x into forest. Balanced subtrees are */ +/* treated as leaves. This potentially adds one to the depth */ +/* of the final tree. */ +void CORD_balance_insert(CORD x, size_t len, ForestElement * forest) +{ + register int depth; + + if (CORD_IS_STRING(x)) { + CORD_add_forest(forest, x, len); + } else if (IS_CONCATENATION(x) + && ((depth = DEPTH(x)) >= MAX_DEPTH + || len < min_len[depth])) { + register struct Concatenation * conc + = &(((CordRep *)x) -> concatenation); + size_t left_len = LEFT_LEN(conc); + + CORD_balance_insert(conc -> left, left_len, forest); + CORD_balance_insert(conc -> right, len - left_len, forest); + } else /* function or balanced */ { + CORD_add_forest(forest, x, len); + } +} + + +CORD CORD_balance(CORD x) +{ + Forest forest; + register size_t len; + + if (x == 0) return(0); + if (CORD_IS_STRING(x)) return(x); + if (!min_len_init) CORD_init_min_len(); + len = LEN(x); + CORD_init_forest(forest, len); + CORD_balance_insert(x, len, forest); + return(CORD_concat_forest(forest, len)); +} + + +/* Position primitives */ + +/* Private routines to deal with the hard cases only: */ + +/* P contains a prefix of the path to cur_pos. Extend it to a full */ +/* path and set up leaf info. */ +/* Return 0 if past the end of cord, 1 o.w. */ +void CORD__extend_path(register CORD_pos p) +{ + register struct CORD_pe * current_pe = &(p[0].path[p[0].path_len]); + register CORD top = current_pe -> pe_cord; + register size_t pos = p[0].cur_pos; + register size_t top_pos = current_pe -> pe_start_pos; + register size_t top_len = GEN_LEN(top); + + /* Fill in the rest of the path. */ + while(!CORD_IS_STRING(top) && IS_CONCATENATION(top)) { + register struct Concatenation * conc = + &(((CordRep *)top) -> concatenation); + register size_t left_len; + + left_len = LEFT_LEN(conc); + current_pe++; + if (pos >= top_pos + left_len) { + current_pe -> pe_cord = top = conc -> right; + current_pe -> pe_start_pos = top_pos = top_pos + left_len; + top_len -= left_len; + } else { + current_pe -> pe_cord = top = conc -> left; + current_pe -> pe_start_pos = top_pos; + top_len = left_len; + } + p[0].path_len++; + } + /* Fill in leaf description for fast access. */ + if (CORD_IS_STRING(top)) { + p[0].cur_leaf = top; + p[0].cur_start = top_pos; + p[0].cur_end = top_pos + top_len; + } else { + p[0].cur_end = 0; + } + if (pos >= top_pos + top_len) p[0].path_len = CORD_POS_INVALID; +} + +char CORD__pos_fetch(register CORD_pos p) +{ + /* Leaf is a function node */ + struct CORD_pe * pe = &((p)[0].path[(p)[0].path_len]); + CORD leaf = pe -> pe_cord; + register struct Function * f = &(((CordRep *)leaf) -> function); + + if (!IS_FUNCTION(leaf)) ABORT("CORD_pos_fetch: bad leaf"); + return ((*(f -> fn))(p[0].cur_pos - pe -> pe_start_pos, f -> client_data)); +} + +void CORD__next(register CORD_pos p) +{ + register size_t cur_pos = p[0].cur_pos + 1; + register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + register CORD leaf = current_pe -> pe_cord; + + /* Leaf is not a string or we're at end of leaf */ + p[0].cur_pos = cur_pos; + if (!CORD_IS_STRING(leaf)) { + /* Function leaf */ + register struct Function * f = &(((CordRep *)leaf) -> function); + register size_t start_pos = current_pe -> pe_start_pos; + register size_t end_pos = start_pos + f -> len; + + if (cur_pos < end_pos) { + /* Fill cache and return. */ + register size_t i; + register size_t limit = cur_pos + FUNCTION_BUF_SZ; + register CORD_fn fn = f -> fn; + register void * client_data = f -> client_data; + + if (limit > end_pos) { + limit = end_pos; + } + for (i = cur_pos; i < limit; i++) { + p[0].function_buf[i - cur_pos] = + (*fn)(i - start_pos, client_data); + } + p[0].cur_start = cur_pos; + p[0].cur_leaf = p[0].function_buf; + p[0].cur_end = limit; + return; + } + } + /* End of leaf */ + /* Pop the stack until we find two concatenation nodes with the */ + /* same start position: this implies we were in left part. */ + { + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos != current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + if (p[0].path_len == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +void CORD__prev(register CORD_pos p) +{ + register struct CORD_pe * pe = &(p[0].path[p[0].path_len]); + + if (p[0].cur_pos == 0) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].cur_pos--; + if (p[0].cur_pos >= pe -> pe_start_pos) return; + + /* Beginning of leaf */ + + /* Pop the stack until we find two concatenation nodes with the */ + /* different start position: this implies we were in right part. */ + { + register struct CORD_pe * current_pe = &((p)[0].path[(p)[0].path_len]); + + while (p[0].path_len > 0 + && current_pe[0].pe_start_pos == current_pe[-1].pe_start_pos) { + p[0].path_len--; + current_pe--; + } + } + p[0].path_len--; + CORD__extend_path(p); +} + +#undef CORD_pos_fetch +#undef CORD_next +#undef CORD_prev +#undef CORD_pos_to_index +#undef CORD_pos_to_cord +#undef CORD_pos_valid + +char CORD_pos_fetch(register CORD_pos p) +{ + if (p[0].cur_start <= p[0].cur_pos && p[0].cur_pos < p[0].cur_end) { + return(p[0].cur_leaf[p[0].cur_pos - p[0].cur_start]); + } else { + return(CORD__pos_fetch(p)); + } +} + +void CORD_next(CORD_pos p) +{ + if (p[0].cur_pos < p[0].cur_end - 1) { + p[0].cur_pos++; + } else { + CORD__next(p); + } +} + +void CORD_prev(CORD_pos p) +{ + if (p[0].cur_end != 0 && p[0].cur_pos > p[0].cur_start) { + p[0].cur_pos--; + } else { + CORD__prev(p); + } +} + +size_t CORD_pos_to_index(CORD_pos p) +{ + return(p[0].cur_pos); +} + +CORD CORD_pos_to_cord(CORD_pos p) +{ + return(p[0].path[0].pe_cord); +} + +int CORD_pos_valid(CORD_pos p) +{ + return(p[0].path_len != CORD_POS_INVALID); +} + +void CORD_set_pos(CORD_pos p, CORD x, size_t i) +{ + if (x == CORD_EMPTY) { + p[0].path_len = CORD_POS_INVALID; + return; + } + p[0].path[0].pe_cord = x; + p[0].path[0].pe_start_pos = 0; + p[0].path_len = 0; + p[0].cur_pos = i; + CORD__extend_path(p); +} diff --git a/gc/cord/cordprnt.c b/gc/cord/cordprnt.c new file mode 100644 index 0000000..9c8cc87 --- /dev/null +++ b/gc/cord/cordprnt.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An sprintf implementation that understands cords. This is probably */ +/* not terribly portable. It assumes an ANSI stdarg.h. It further */ +/* assumes that I can make copies of va_list variables, and read */ +/* arguments repeatedly by applyting va_arg to the copies. This */ +/* could be avoided at some performance cost. */ +/* We also assume that unsigned and signed integers of various kinds */ +/* have the same sizes, and can be cast back and forth. */ +/* We assume that void * and char * have the same size. */ +/* All this cruft is needed because we want to rely on the underlying */ +/* sprintf implementation whenever possible. */ +/* Boehm, September 21, 1995 6:00 pm PDT */ + +#include "cord.h" +#include "ec.h" +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "gc.h" + +#define CONV_SPEC_LEN 50 /* Maximum length of a single */ + /* conversion specification. */ +#define CONV_RESULT_LEN 50 /* Maximum length of any */ + /* conversion with default */ + /* width and prec. */ + + +static int ec_len(CORD_ec x) +{ + return(CORD_len(x[0].ec_cord) + (x[0].ec_bufptr - x[0].ec_buf)); +} + +/* Possible nonumeric precision values. */ +# define NONE -1 +# define VARIABLE -2 +/* Copy the conversion specification from CORD_pos into the buffer buf */ +/* Return negative on error. */ +/* Source initially points one past the leading %. */ +/* It is left pointing at the conversion type. */ +/* Assign field width and precision to *width and *prec. */ +/* If width or prec is *, VARIABLE is assigned. */ +/* Set *left to 1 if left adjustment flag is present. */ +/* Set *long_arg to 1 if long flag ('l' or 'L') is present, or to */ +/* -1 if 'h' is present. */ +static int extract_conv_spec(CORD_pos source, char *buf, + int * width, int *prec, int *left, int * long_arg) +{ + register int result = 0; + register int current_number = 0; + register int saw_period = 0; + register int saw_number; + register int chars_so_far = 0; + register char current; + + *width = NONE; + buf[chars_so_far++] = '%'; + while(CORD_pos_valid(source)) { + if (chars_so_far >= CONV_SPEC_LEN) return(-1); + current = CORD_pos_fetch(source); + buf[chars_so_far++] = current; + switch(current) { + case '*': + saw_number = 1; + current_number = VARIABLE; + break; + case '0': + if (!saw_number) { + /* Zero fill flag; ignore */ + break; + } /* otherwise fall through: */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + saw_number = 1; + current_number *= 10; + current_number += current - '0'; + break; + case '.': + saw_period = 1; + if(saw_number) { + *width = current_number; + saw_number = 0; + } + current_number = 0; + break; + case 'l': + case 'L': + *long_arg = 1; + current_number = 0; + break; + case 'h': + *long_arg = -1; + current_number = 0; + break; + case ' ': + case '+': + case '#': + current_number = 0; + break; + case '-': + *left = 1; + current_number = 0; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + case 'c': + case 'C': + case 's': + case 'S': + case 'p': + case 'n': + case 'r': + goto done; + default: + return(-1); + } + CORD_next(source); + } + return(-1); + done: + if (saw_number) { + if (saw_period) { + *prec = current_number; + } else { + *prec = NONE; + *width = current_number; + } + } else { + *prec = NONE; + } + buf[chars_so_far] = '\0'; + return(result); +} + +int CORD_vsprintf(CORD * out, CORD format, va_list args) +{ + CORD_ec result; + register int count; + register char current; + CORD_pos pos; + char conv_spec[CONV_SPEC_LEN + 1]; + + CORD_ec_init(result); + for (CORD_set_pos(pos, format, 0); CORD_pos_valid(pos); CORD_next(pos)) { + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_next(pos); + if (!CORD_pos_valid(pos)) return(-1); + current = CORD_pos_fetch(pos); + if (current == '%') { + CORD_ec_append(result, current); + } else { + int width, prec; + int left_adj = 0; + int long_arg = 0; + CORD arg; + size_t len; + + if (extract_conv_spec(pos, conv_spec, + &width, &prec, + &left_adj, &long_arg) < 0) { + return(-1); + } + current = CORD_pos_fetch(pos); + switch(current) { + case 'n': + /* Assign length to next arg */ + if (long_arg == 0) { + int * pos_ptr; + pos_ptr = va_arg(args, int *); + *pos_ptr = ec_len(result); + } else if (long_arg > 0) { + long * pos_ptr; + pos_ptr = va_arg(args, long *); + *pos_ptr = ec_len(result); + } else { + short * pos_ptr; + pos_ptr = va_arg(args, short *); + *pos_ptr = ec_len(result); + } + goto done; + case 'r': + /* Append cord and any padding */ + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + arg = va_arg(args, CORD); + len = CORD_len(arg); + if (prec != NONE && len > prec) { + if (prec < 0) return(-1); + arg = CORD_substr(arg, 0, prec); + len = prec; + } + if (width != NONE && len < width) { + char * blanks = GC_MALLOC_ATOMIC(width-len+1); + + memset(blanks, ' ', width-len); + blanks[width-len] = '\0'; + if (left_adj) { + arg = CORD_cat(arg, blanks); + } else { + arg = CORD_cat(blanks, arg); + } + } + CORD_ec_append_cord(result, arg); + goto done; + case 'c': + if (width == NONE && prec == NONE) { + register char c; + + c = va_arg(args, char); + CORD_ec_append(result, c); + goto done; + } + break; + case 's': + if (width == NONE && prec == NONE) { + char * str = va_arg(args, char *); + register char c; + + while (c = *str++) { + CORD_ec_append(result, c); + } + goto done; + } + break; + default: + break; + } + /* Use standard sprintf to perform conversion */ + { + register char * buf; + va_list vsprintf_args = args; + /* The above does not appear to be sanctioned */ + /* by the ANSI C standard. */ + int max_size = 0; + int res; + + if (width == VARIABLE) width = va_arg(args, int); + if (prec == VARIABLE) prec = va_arg(args, int); + if (width != NONE) max_size = width; + if (prec != NONE && prec > max_size) max_size = prec; + max_size += CONV_RESULT_LEN; + if (max_size >= CORD_BUFSZ) { + buf = GC_MALLOC_ATOMIC(max_size + 1); + } else { + if (CORD_BUFSZ - (result[0].ec_bufptr-result[0].ec_buf) + < max_size) { + CORD_ec_flush_buf(result); + } + buf = result[0].ec_bufptr; + } + switch(current) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + case 'c': + if (long_arg <= 0) { + (void) va_arg(args, int); + } else if (long_arg > 0) { + (void) va_arg(args, long); + } + break; + case 's': + case 'p': + (void) va_arg(args, char *); + break; + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + (void) va_arg(args, double); + break; + default: + return(-1); + } + res = vsprintf(buf, conv_spec, vsprintf_args); + len = (size_t)res; + if ((char *)(GC_word)res == buf) { + /* old style vsprintf */ + len = strlen(buf); + } else if (res < 0) { + return(-1); + } + if (buf != result[0].ec_bufptr) { + register char c; + + while (c = *buf++) { + CORD_ec_append(result, c); + } + } else { + result[0].ec_bufptr = buf + len; + } + } + done:; + } + } else { + CORD_ec_append(result, current); + } + } + count = ec_len(result); + *out = CORD_balance(CORD_ec_to_cord(result)); + return(count); +} + +int CORD_sprintf(CORD * out, CORD format, ...) +{ + va_list args; + int result; + + va_start(args, format); + result = CORD_vsprintf(out, format, args); + va_end(args); + return(result); +} + +int CORD_fprintf(FILE * f, CORD format, ...) +{ + va_list args; + int result; + CORD out; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_vfprintf(FILE * f, CORD format, va_list args) +{ + int result; + CORD out; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, f); + return(result); +} + +int CORD_printf(CORD format, ...) +{ + va_list args; + int result; + CORD out; + + va_start(args, format); + result = CORD_vsprintf(&out, format, args); + va_end(args); + if (result > 0) CORD_put(out, stdout); + return(result); +} + +int CORD_vprintf(CORD format, va_list args) +{ + int result; + CORD out; + + result = CORD_vsprintf(&out, format, args); + if (result > 0) CORD_put(out, stdout); + return(result); +} diff --git a/gc/cord/cordtest.c b/gc/cord/cordtest.c new file mode 100644 index 0000000..d11d7dd --- /dev/null +++ b/gc/cord/cordtest.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, August 24, 1994 11:58 am PDT */ +# include "cord.h" +# include <string.h> +# include <stdio.h> +/* This is a very incomplete test of the cord package. It knows about */ +/* a few internals of the package (e.g. when C strings are returned) */ +/* that real clients shouldn't rely on. */ + +# define ABORT(string) \ +{ int x = 0; fprintf(stderr, "FAILED: %s\n", string); x = 1 / x; abort(); } + +int count; + +int test_fn(char c, void * client_data) +{ + if (client_data != (void *)13) ABORT("bad client data"); + if (count < 64*1024+1) { + if ((count & 1) == 0) { + if (c != 'b') ABORT("bad char"); + } else { + if (c != 'a') ABORT("bad char"); + } + count++; + return(0); + } else { + if (c != 'c') ABORT("bad char"); + count++; + return(1); + } +} + +char id_cord_fn(size_t i, void * client_data) +{ + return((char)i); +} + +void test_basics() +{ + CORD x = CORD_from_char_star("ab"); + register int i; + char c; + CORD y; + CORD_pos p; + + x = CORD_cat(x,x); + if (!CORD_IS_STRING(x)) ABORT("short cord should usually be a string"); + if (strcmp(x, "abab") != 0) ABORT("bad CORD_cat result"); + + for (i = 1; i < 16; i++) { + x = CORD_cat(x,x); + } + x = CORD_cat(x,"c"); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + count = 0; + CORD_set_pos(p, x, 64*1024-1); + while(CORD_pos_valid(p)) { + (void) test_fn(CORD_pos_fetch(p), (void *)13); + CORD_next(p); + } + if (count != 64*1024 + 2) ABORT("Position based iteration failed"); + + y = CORD_substr(x, 1023, 5); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 1024, 8); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "abababab") != 0) ABORT("bad CORD_substr result"); + + y = CORD_substr(x, 128*1024-1, 8); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "bc") != 0) ABORT("bad CORD_substr result"); + + x = CORD_balance(x); + if (CORD_len(x) != 128*1024+1) ABORT("bad length"); + + count = 0; + if (CORD_iter5(x, 64*1024-1, test_fn, CORD_NO_FN, (void *)13) == 0) { + ABORT("CORD_iter5 failed"); + } + if (count != 64*1024 + 2) ABORT("CORD_iter5 failed"); + + y = CORD_substr(x, 1023, 5); + if (!CORD_IS_STRING(y)) ABORT("short cord should usually be a string"); + if (strcmp(y, "babab") != 0) ABORT("bad CORD_substr result"); + y = CORD_from_fn(id_cord_fn, 0, 13); + i = 0; + CORD_set_pos(p, y, i); + while(CORD_pos_valid(p)) { + c = CORD_pos_fetch(p); + if(c != i) ABORT("Traversal of function node failed"); + CORD_next(p); i++; + } + if (i != 13) ABORT("Bad apparent length for function node"); +} + +void test_extras() +{ +# if defined(__OS2__) +# define FNAME1 "tmp1" +# define FNAME2 "tmp2" +# elif defined(AMIGA) +# define FNAME1 "T:tmp1" +# define FNAME2 "T:tmp2" +# else +# define FNAME1 "/tmp/cord_test" +# define FNAME2 "/tmp/cord_test2" +# endif + register int i; + CORD y = "abcdefghijklmnopqrstuvwxyz0123456789"; + CORD x = "{}"; + CORD w, z; + FILE *f; + FILE *f1a, *f1b, *f2; + + w = CORD_cat(CORD_cat(y,y),y); + z = CORD_catn(3,y,y,y); + if (CORD_cmp(w,z) != 0) ABORT("CORD_catn comparison wrong"); + for (i = 1; i < 100; i++) { + x = CORD_cat(x, y); + } + z = CORD_balance(x); + if (CORD_cmp(x,z) != 0) ABORT("balanced string comparison wrong"); + if (CORD_cmp(x,CORD_cat(z, CORD_nul(13))) >= 0) ABORT("comparison 2"); + if (CORD_cmp(CORD_cat(x, CORD_nul(13)), z) <= 0) ABORT("comparison 3"); + if (CORD_cmp(x,CORD_cat(z, "13")) >= 0) ABORT("comparison 4"); + if ((f = fopen(FNAME1, "w")) == 0) ABORT("open failed"); + if (CORD_put(z,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + w = CORD_from_file(f1a = fopen(FNAME1, "rb")); + if (CORD_len(w) != CORD_len(z)) ABORT("file length wrong"); + if (CORD_cmp(w,z) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 50*36+2, 36), y) != 0) + ABORT("file substr wrong"); + z = CORD_from_file_lazy(f1b = fopen(FNAME1, "rb")); + if (CORD_cmp(w,z) != 0) ABORT("File conversions differ"); + if (CORD_chr(w, 0, '9') != 37) ABORT("CORD_chr failed 1"); + if (CORD_chr(w, 3, 'a') != 38) ABORT("CORD_chr failed 2"); + if (CORD_rchr(w, CORD_len(w) - 1, '}') != 1) ABORT("CORD_rchr failed"); + x = y; + for (i = 1; i < 14; i++) { + x = CORD_cat(x,x); + } + if ((f = fopen(FNAME2, "w")) == 0) ABORT("2nd open failed"); + if (CORD_put(x,f) == EOF) ABORT("CORD_put failed"); + if (fclose(f) == EOF) ABORT("fclose failed"); + w = CORD_from_file(f2 = fopen(FNAME2, "rb")); + if (CORD_len(w) != CORD_len(x)) ABORT("file length wrong"); + if (CORD_cmp(w,x) != 0) ABORT("file comparison wrong"); + if (CORD_cmp(CORD_substr(w, 1000*36, 36), y) != 0) + ABORT("file substr wrong"); + if (strcmp(CORD_to_char_star(CORD_substr(w, 1000*36, 36)), y) != 0) + ABORT("char * file substr wrong"); + if (strcmp(CORD_substr(w, 1000*36, 2), "ab") != 0) + ABORT("short file substr wrong"); + if (CORD_str(x,1,"9a") != 35) ABORT("CORD_str failed 1"); + if (CORD_str(x,0,"9abcdefghijk") != 35) ABORT("CORD_str failed 2"); + if (CORD_str(x,0,"9abcdefghijx") != CORD_NOT_FOUND) + ABORT("CORD_str failed 3"); + if (CORD_str(x,0,"9>") != CORD_NOT_FOUND) ABORT("CORD_str failed 4"); + if (remove(FNAME1) != 0) { + /* On some systems, e.g. OS2, this may fail if f1 is still open. */ + if ((fclose(f1a) == EOF) & (fclose(f1b) == EOF)) + ABORT("fclose(f1) failed"); + if (remove(FNAME1) != 0) ABORT("remove 1 failed"); + } + if (remove(FNAME2) != 0) { + if (fclose(f2) == EOF) ABORT("fclose(f2) failed"); + if (remove(FNAME2) != 0) ABORT("remove 2 failed"); + } +} + +void test_printf() +{ + CORD result; + char result2[200]; + long l; + short s; + CORD x; + + if (CORD_sprintf(&result, "%7.2f%ln", 3.14159F, &l) != 7) + ABORT("CORD_sprintf failed 1"); + if (CORD_cmp(result, " 3.14") != 0)ABORT("CORD_sprintf goofed 1"); + if (l != 7) ABORT("CORD_sprintf goofed 2"); + if (CORD_sprintf(&result, "%-7.2s%hn%c%s", "abcd", &s, 'x', "yz") != 10) + ABORT("CORD_sprintf failed 2"); + if (CORD_cmp(result, "ab xyz") != 0)ABORT("CORD_sprintf goofed 3"); + if (s != 7) ABORT("CORD_sprintf goofed 4"); + x = "abcdefghij"; + x = CORD_cat(x,x); + x = CORD_cat(x,x); + x = CORD_cat(x,x); + if (CORD_sprintf(&result, "->%-120.78r!\n", x) != 124) + ABORT("CORD_sprintf failed 3"); + (void) sprintf(result2, "->%-120.78s!\n", CORD_to_char_star(x)); + if (CORD_cmp(result, result2) != 0)ABORT("CORD_sprintf goofed 5"); +} + +main() +{ +# ifdef THINK_C + printf("cordtest:\n"); +# endif + test_basics(); + test_extras(); + test_printf(); + CORD_fprintf(stderr, "SUCCEEDED\n"); + return(0); +} diff --git a/gc/cord/cordxtra.c b/gc/cord/cordxtra.c new file mode 100644 index 0000000..a5be10d --- /dev/null +++ b/gc/cord/cordxtra.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* + * These are functions on cords that do not need to understand their + * implementation. They serve also serve as example client code for + * cord_basics. + */ +/* Boehm, December 8, 1995 1:53 pm PST */ +# include <stdio.h> +# include <string.h> +# include <stdlib.h> +# include <stdarg.h> +# include "cord.h" +# include "ec.h" +# define I_HIDE_POINTERS /* So we get access to allocation lock. */ + /* We use this for lazy file reading, */ + /* so that we remain independent */ + /* of the threads primitives. */ +# include "gc.h" + +/* For now we assume that pointer reads and writes are atomic, */ +/* i.e. another thread always sees the state before or after */ +/* a write. This might be false on a Motorola M68K with */ +/* pointers that are not 32-bit aligned. But there probably */ +/* aren't too many threads packages running on those. */ +# define ATOMIC_WRITE(x,y) (x) = (y) +# define ATOMIC_READ(x) (*(x)) + +/* The standard says these are in stdio.h, but they aren't always: */ +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif +# ifndef SEEK_END +# define SEEK_END 2 +# endif + +# define BUFSZ 2048 /* Size of stack allocated buffers when */ + /* we want large buffers. */ + +typedef void (* oom_fn)(void); + +# define OUT_OF_MEMORY { if (CORD_oom_fn != (oom_fn) 0) (*CORD_oom_fn)(); \ + ABORT("Out of memory\n"); } +# define ABORT(msg) { fprintf(stderr, "%s\n", msg); abort(); } + +CORD CORD_cat_char(CORD x, char c) +{ + register char * string; + + if (c == '\0') return(CORD_cat(x, CORD_nul(1))); + string = GC_MALLOC_ATOMIC(2); + if (string == 0) OUT_OF_MEMORY; + string[0] = c; + string[1] = '\0'; + return(CORD_cat_char_star(x, string, 1)); +} + +CORD CORD_catn(int nargs, ...) +{ + register CORD result = CORD_EMPTY; + va_list args; + register int i; + + va_start(args, nargs); + for (i = 0; i < nargs; i++) { + register CORD next = va_arg(args, CORD); + result = CORD_cat(result, next); + } + va_end(args); + return(result); +} + +typedef struct { + size_t len; + size_t count; + char * buf; +} CORD_fill_data; + +int CORD_fill_proc(char c, void * client_data) +{ + register CORD_fill_data * d = (CORD_fill_data *)client_data; + register size_t count = d -> count; + + (d -> buf)[count] = c; + d -> count = ++count; + if (count >= d -> len) { + return(1); + } else { + return(0); + } +} + +int CORD_batched_fill_proc(const char * s, void * client_data) +{ + register CORD_fill_data * d = (CORD_fill_data *)client_data; + register size_t count = d -> count; + register size_t max = d -> len; + register char * buf = d -> buf; + register const char * t = s; + + while((buf[count] = *t++) != '\0') { + count++; + if (count >= max) { + d -> count = count; + return(1); + } + } + d -> count = count; + return(0); +} + +/* Fill buf with len characters starting at i. */ +/* Assumes len characters are available. */ +void CORD_fill_buf(CORD x, size_t i, size_t len, char * buf) +{ + CORD_fill_data fd; + + fd.len = len; + fd.buf = buf; + fd.count = 0; + (void)CORD_iter5(x, i, CORD_fill_proc, CORD_batched_fill_proc, &fd); +} + +int CORD_cmp(CORD x, CORD y) +{ + CORD_pos xpos; + CORD_pos ypos; + register size_t avail, yavail; + + if (y == CORD_EMPTY) return(x != CORD_EMPTY); + if (x == CORD_EMPTY) return(-1); + if (CORD_IS_STRING(y) && CORD_IS_STRING(x)) return(strcmp(x,y)); + CORD_set_pos(xpos, x, 0); + CORD_set_pos(ypos, y, 0); + for(;;) { + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + if ((avail = CORD_pos_chars_left(xpos)) <= 0 + || (yavail = CORD_pos_chars_left(ypos)) <= 0) { + register char xcurrent = CORD_pos_fetch(xpos); + register char ycurrent = CORD_pos_fetch(ypos); + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + } else { + /* process as many characters as we can */ + register int result; + + if (avail > yavail) avail = yavail; + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, avail); + CORD_pos_advance(ypos, avail); + } + } +} + +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len) +{ + CORD_pos xpos; + CORD_pos ypos; + register size_t count; + register long avail, yavail; + + CORD_set_pos(xpos, x, x_start); + CORD_set_pos(ypos, y, y_start); + for(count = 0; count < len;) { + if (!CORD_pos_valid(xpos)) { + if (CORD_pos_valid(ypos)) { + return(-1); + } else { + return(0); + } + } + if (!CORD_pos_valid(ypos)) { + return(1); + } + if ((avail = CORD_pos_chars_left(xpos)) <= 0 + || (yavail = CORD_pos_chars_left(ypos)) <= 0) { + register char xcurrent = CORD_pos_fetch(xpos); + register char ycurrent = CORD_pos_fetch(ypos); + if (xcurrent != ycurrent) return(xcurrent - ycurrent); + CORD_next(xpos); + CORD_next(ypos); + count++; + } else { + /* process as many characters as we can */ + register int result; + + if (avail > yavail) avail = yavail; + count += avail; + if (count > len) avail -= (count - len); + result = strncmp(CORD_pos_cur_char_addr(xpos), + CORD_pos_cur_char_addr(ypos), (size_t)avail); + if (result != 0) return(result); + CORD_pos_advance(xpos, (size_t)avail); + CORD_pos_advance(ypos, (size_t)avail); + } + } + return(0); +} + +char * CORD_to_char_star(CORD x) +{ + register size_t len = CORD_len(x); + char * result = GC_MALLOC_ATOMIC(len + 1); + + if (result == 0) OUT_OF_MEMORY; + CORD_fill_buf(x, 0, len, result); + result[len] = '\0'; + return(result); +} + +CORD CORD_from_char_star(const char *s) +{ + char * result; + size_t len = strlen(s); + + if (0 == len) return(CORD_EMPTY); + result = GC_MALLOC_ATOMIC(len + 1); + if (result == 0) OUT_OF_MEMORY; + memcpy(result, s, len+1); + return(result); +} + +const char * CORD_to_const_char_star(CORD x) +{ + if (x == 0) return(""); + if (CORD_IS_STRING(x)) return((const char *)x); + return(CORD_to_char_star(x)); +} + +char CORD_fetch(CORD x, size_t i) +{ + CORD_pos xpos; + + CORD_set_pos(xpos, x, i); + if (!CORD_pos_valid(xpos)) ABORT("bad index?"); + return(CORD_pos_fetch(xpos)); +} + + +int CORD_put_proc(char c, void * client_data) +{ + register FILE * f = (FILE *)client_data; + + return(putc(c, f) == EOF); +} + +int CORD_batched_put_proc(const char * s, void * client_data) +{ + register FILE * f = (FILE *)client_data; + + return(fputs(s, f) == EOF); +} + + +int CORD_put(CORD x, FILE * f) +{ + if (CORD_iter5(x, 0, CORD_put_proc, CORD_batched_put_proc, f)) { + return(EOF); + } else { + return(1); + } +} + +typedef struct { + size_t pos; /* Current position in the cord */ + char target; /* Character we're looking for */ +} chr_data; + +int CORD_chr_proc(char c, void * client_data) +{ + register chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) ++; + return(0); +} + +int CORD_rchr_proc(char c, void * client_data) +{ + register chr_data * d = (chr_data *)client_data; + + if (c == d -> target) return(1); + (d -> pos) --; + return(0); +} + +int CORD_batched_chr_proc(const char *s, void * client_data) +{ + register chr_data * d = (chr_data *)client_data; + register char * occ = strchr(s, d -> target); + + if (occ == 0) { + d -> pos += strlen(s); + return(0); + } else { + d -> pos += occ - s; + return(1); + } +} + +size_t CORD_chr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = c; + if (CORD_iter5(x, i, CORD_chr_proc, CORD_batched_chr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +size_t CORD_rchr(CORD x, size_t i, int c) +{ + chr_data d; + + d.pos = i; + d.target = c; + if (CORD_riter4(x, i, CORD_rchr_proc, &d)) { + return(d.pos); + } else { + return(CORD_NOT_FOUND); + } +} + +/* Find the first occurrence of s in x at position start or later. */ +/* This uses an asymptotically poor algorithm, which should typically */ +/* perform acceptably. We compare the first few characters directly, */ +/* and call CORD_ncmp whenever there is a partial match. */ +/* This has the advantage that we allocate very little, or not at all. */ +/* It's very fast if there are few close misses. */ +size_t CORD_str(CORD x, size_t start, CORD s) +{ + CORD_pos xpos; + size_t xlen = CORD_len(x); + size_t slen; + register size_t start_len; + const char * s_start; + unsigned long s_buf = 0; /* The first few characters of s */ + unsigned long x_buf = 0; /* Start of candidate substring. */ + /* Initialized only to make compilers */ + /* happy. */ + unsigned long mask = 0; + register size_t i; + register size_t match_pos; + + if (s == CORD_EMPTY) return(start); + if (CORD_IS_STRING(s)) { + s_start = s; + slen = strlen(s); + } else { + s_start = CORD_to_char_star(CORD_substr(s, 0, sizeof(unsigned long))); + slen = CORD_len(s); + } + if (xlen < start || xlen - start < slen) return(CORD_NOT_FOUND); + start_len = slen; + if (start_len > sizeof(unsigned long)) start_len = sizeof(unsigned long); + CORD_set_pos(xpos, x, start); + for (i = 0; i < start_len; i++) { + mask <<= 8; + mask |= 0xff; + s_buf <<= 8; + s_buf |= s_start[i]; + x_buf <<= 8; + x_buf |= CORD_pos_fetch(xpos); + CORD_next(xpos); + } + for (match_pos = start; ; match_pos++) { + if ((x_buf & mask) == s_buf) { + if (slen == start_len || + CORD_ncmp(x, match_pos + start_len, + s, start_len, slen - start_len) == 0) { + return(match_pos); + } + } + if ( match_pos == xlen - slen ) { + return(CORD_NOT_FOUND); + } + x_buf <<= 8; + x_buf |= CORD_pos_fetch(xpos); + CORD_next(xpos); + } +} + +void CORD_ec_flush_buf(CORD_ec x) +{ + register size_t len = x[0].ec_bufptr - x[0].ec_buf; + char * s; + + if (len == 0) return; + s = GC_MALLOC_ATOMIC(len+1); + memcpy(s, x[0].ec_buf, len); + s[len] = '\0'; + x[0].ec_cord = CORD_cat_char_star(x[0].ec_cord, s, len); + x[0].ec_bufptr = x[0].ec_buf; +} + +void CORD_ec_append_cord(CORD_ec x, CORD s) +{ + CORD_ec_flush_buf(x); + x[0].ec_cord = CORD_cat(x[0].ec_cord, s); +} + +/*ARGSUSED*/ +char CORD_nul_func(size_t i, void * client_data) +{ + return((char)(unsigned long)client_data); +} + + +CORD CORD_chars(char c, size_t i) +{ + return(CORD_from_fn(CORD_nul_func, (void *)(unsigned long)c, i)); +} + +CORD CORD_from_file_eager(FILE * f) +{ + register int c; + CORD_ec ecord; + + CORD_ec_init(ecord); + for(;;) { + c = getc(f); + if (c == 0) { + /* Append the right number of NULs */ + /* Note that any string of NULs is rpresented in 4 words, */ + /* independent of its length. */ + register size_t count = 1; + + CORD_ec_flush_buf(ecord); + while ((c = getc(f)) == 0) count++; + ecord[0].ec_cord = CORD_cat(ecord[0].ec_cord, CORD_nul(count)); + } + if (c == EOF) break; + CORD_ec_append(ecord, c); + } + (void) fclose(f); + return(CORD_balance(CORD_ec_to_cord(ecord))); +} + +/* The state maintained for a lazily read file consists primarily */ +/* of a large direct-mapped cache of previously read values. */ +/* We could rely more on stdio buffering. That would have 2 */ +/* disadvantages: */ +/* 1) Empirically, not all fseek implementations preserve the */ +/* buffer whenever they could. */ +/* 2) It would fail if 2 different sections of a long cord */ +/* were being read alternately. */ +/* We do use the stdio buffer for read ahead. */ +/* To guarantee thread safety in the presence of atomic pointer */ +/* writes, cache lines are always replaced, and never modified in */ +/* place. */ + +# define LOG_CACHE_SZ 14 +# define CACHE_SZ (1 << LOG_CACHE_SZ) +# define LOG_LINE_SZ 9 +# define LINE_SZ (1 << LOG_LINE_SZ) + +typedef struct { + size_t tag; + char data[LINE_SZ]; + /* data[i%LINE_SZ] = ith char in file if tag = i/LINE_SZ */ +} cache_line; + +typedef struct { + FILE * lf_file; + size_t lf_current; /* Current file pointer value */ + cache_line * volatile lf_cache[CACHE_SZ/LINE_SZ]; +} lf_state; + +# define MOD_CACHE_SZ(n) ((n) & (CACHE_SZ - 1)) +# define DIV_CACHE_SZ(n) ((n) >> LOG_CACHE_SZ) +# define MOD_LINE_SZ(n) ((n) & (LINE_SZ - 1)) +# define DIV_LINE_SZ(n) ((n) >> LOG_LINE_SZ) +# define LINE_START(n) ((n) & ~(LINE_SZ - 1)) + +typedef struct { + lf_state * state; + size_t file_pos; /* Position of needed character. */ + cache_line * new_cache; +} refill_data; + +/* Executed with allocation lock. */ +static char refill_cache(client_data) +refill_data * client_data; +{ + register lf_state * state = client_data -> state; + register size_t file_pos = client_data -> file_pos; + FILE *f = state -> lf_file; + size_t line_start = LINE_START(file_pos); + size_t line_no = DIV_LINE_SZ(MOD_CACHE_SZ(file_pos)); + cache_line * new_cache = client_data -> new_cache; + + if (line_start != state -> lf_current + && fseek(f, line_start, SEEK_SET) != 0) { + ABORT("fseek failed"); + } + if (fread(new_cache -> data, sizeof(char), LINE_SZ, f) + <= file_pos - line_start) { + ABORT("fread failed"); + } + new_cache -> tag = DIV_LINE_SZ(file_pos); + /* Store barrier goes here. */ + ATOMIC_WRITE(state -> lf_cache[line_no], new_cache); + state -> lf_current = line_start + LINE_SZ; + return(new_cache->data[MOD_LINE_SZ(file_pos)]); +} + +char CORD_lf_func(size_t i, void * client_data) +{ + register lf_state * state = (lf_state *)client_data; + register cache_line * volatile * cl_addr = + &(state -> lf_cache[DIV_LINE_SZ(MOD_CACHE_SZ(i))]); + register cache_line * cl = (cache_line *)ATOMIC_READ(cl_addr); + + if (cl == 0 || cl -> tag != DIV_LINE_SZ(i)) { + /* Cache miss */ + refill_data rd; + + rd.state = state; + rd.file_pos = i; + rd.new_cache = GC_NEW_ATOMIC(cache_line); + if (rd.new_cache == 0) OUT_OF_MEMORY; + return((char)(GC_word) + GC_call_with_alloc_lock((GC_fn_type) refill_cache, &rd)); + } + return(cl -> data[MOD_LINE_SZ(i)]); +} + +/*ARGSUSED*/ +void CORD_lf_close_proc(void * obj, void * client_data) +{ + if (fclose(((lf_state *)obj) -> lf_file) != 0) { + ABORT("CORD_lf_close_proc: fclose failed"); + } +} + +CORD CORD_from_file_lazy_inner(FILE * f, size_t len) +{ + register lf_state * state = GC_NEW(lf_state); + register int i; + + if (state == 0) OUT_OF_MEMORY; + if (len != 0) { + /* Dummy read to force buffer allocation. */ + /* This greatly increases the probability */ + /* of avoiding deadlock if buffer allocation */ + /* is redirected to GC_malloc and the */ + /* world is multithreaded. */ + char buf[1]; + + (void) fread(buf, 1, 1, f); + rewind(f); + } + state -> lf_file = f; + for (i = 0; i < CACHE_SZ/LINE_SZ; i++) { + state -> lf_cache[i] = 0; + } + state -> lf_current = 0; + GC_REGISTER_FINALIZER(state, CORD_lf_close_proc, 0, 0, 0); + return(CORD_from_fn(CORD_lf_func, state, len)); +} + +CORD CORD_from_file_lazy(FILE * f) +{ + register long len; + + if (fseek(f, 0l, SEEK_END) != 0) { + ABORT("Bad fd argument - fseek failed"); + } + if ((len = ftell(f)) < 0) { + ABORT("Bad fd argument - ftell failed"); + } + rewind(f); + return(CORD_from_file_lazy_inner(f, (size_t)len)); +} + +# define LAZY_THRESHOLD (128*1024 + 1) + +CORD CORD_from_file(FILE * f) +{ + register long len; + + if (fseek(f, 0l, SEEK_END) != 0) { + ABORT("Bad fd argument - fseek failed"); + } + if ((len = ftell(f)) < 0) { + ABORT("Bad fd argument - ftell failed"); + } + rewind(f); + if (len < LAZY_THRESHOLD) { + return(CORD_from_file_eager(f)); + } else { + return(CORD_from_file_lazy_inner(f, (size_t)len)); + } +} diff --git a/gc/cord/de.c b/gc/cord/de.c new file mode 100644 index 0000000..fda7142 --- /dev/null +++ b/gc/cord/de.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* + * A really simple-minded text editor based on cords. + * Things it does right: + * No size bounds. + * Inbounded undo. + * Shouldn't crash no matter what file you invoke it on (e.g. /vmunix) + * (Make sure /vmunix is not writable before you try this.) + * Scrolls horizontally. + * Things it does wrong: + * It doesn't handle tabs reasonably (use "expand" first). + * The command set is MUCH too small. + * The redisplay algorithm doesn't let curses do the scrolling. + * The rule for moving the window over the file is suboptimal. + */ +/* Boehm, February 6, 1995 12:27 pm PST */ + +/* Boehm, May 19, 1994 2:20 pm PDT */ +#include <stdio.h> +#include "gc.h" +#include "cord.h" + +#ifdef THINK_C +#define MACINTOSH +#include <ctype.h> +#endif + +#if defined(__BORLANDC__) && !defined(WIN32) + /* If this is DOS or win16, we'll fail anyway. */ + /* Might as well assume win32. */ +# define WIN32 +#endif + +#if defined(WIN32) +# include <windows.h> +# include "de_win.h" +#elif defined(MACINTOSH) +# include <console.h> +/* curses emulation. */ +# define initscr() +# define endwin() +# define nonl() +# define noecho() csetmode(C_NOECHO, stdout) +# define cbreak() csetmode(C_CBREAK, stdout) +# define refresh() +# define addch(c) putchar(c) +# define standout() cinverse(1, stdout) +# define standend() cinverse(0, stdout) +# define move(line,col) cgotoxy(col + 1, line + 1, stdout) +# define clrtoeol() ccleol(stdout) +# define de_error(s) { fprintf(stderr, s); getchar(); } +# define LINES 25 +# define COLS 80 +#else +# include <curses.h> +# define de_error(s) { fprintf(stderr, s); sleep(2); } +#endif +#include "de_cmds.h" + +/* List of line number to position mappings, in descending order. */ +/* There may be holes. */ +typedef struct LineMapRep { + int line; + size_t pos; + struct LineMapRep * previous; +} * line_map; + +/* List of file versions, one per edit operation */ +typedef struct HistoryRep { + CORD file_contents; + struct HistoryRep * previous; + line_map map; /* Invalid for first record "now" */ +} * history; + +history now = 0; +CORD current; /* == now -> file_contents. */ +size_t current_len; /* Current file length. */ +line_map current_map = 0; /* Current line no. to pos. map */ +size_t current_map_size = 0; /* Number of current_map entries. */ + /* Not always accurate, but reset */ + /* by prune_map. */ +# define MAX_MAP_SIZE 3000 + +/* Current display position */ +int dis_line = 0; +int dis_col = 0; + +# define ALL -1 +# define NONE - 2 +int need_redisplay = 0; /* Line that needs to be redisplayed. */ + + +/* Current cursor position. Always within file. */ +int line = 0; +int col = 0; +size_t file_pos = 0; /* Character position corresponding to cursor. */ + +/* Invalidate line map for lines > i */ +void invalidate_map(int i) +{ + while(current_map -> line > i) { + current_map = current_map -> previous; + current_map_size--; + } +} + +/* Reduce the number of map entries to save space for huge files. */ +/* This also affects maps in histories. */ +void prune_map() +{ + line_map map = current_map; + int start_line = map -> line; + + current_map_size = 0; + for(; map != 0; map = map -> previous) { + current_map_size++; + if (map -> line < start_line - LINES && map -> previous != 0) { + map -> previous = map -> previous -> previous; + } + } +} +/* Add mapping entry */ +void add_map(int line, size_t pos) +{ + line_map new_map = GC_NEW(struct LineMapRep); + + if (current_map_size >= MAX_MAP_SIZE) prune_map(); + new_map -> line = line; + new_map -> pos = pos; + new_map -> previous = current_map; + current_map = new_map; + current_map_size++; +} + + + +/* Return position of column *c of ith line in */ +/* current file. Adjust *c to be within the line.*/ +/* A 0 pointer is taken as 0 column. */ +/* Returns CORD_NOT_FOUND if i is too big. */ +/* Assumes i > dis_line. */ +size_t line_pos(int i, int *c) +{ + int j; + size_t cur; + size_t next; + line_map map = current_map; + + while (map -> line > i) map = map -> previous; + if (map -> line < i - 2) /* rebuild */ invalidate_map(i); + for (j = map -> line, cur = map -> pos; j < i;) { + cur = CORD_chr(current, cur, '\n'); + if (cur == current_len-1) return(CORD_NOT_FOUND); + cur++; + if (++j > current_map -> line) add_map(j, cur); + } + if (c != 0) { + next = CORD_chr(current, cur, '\n'); + if (next == CORD_NOT_FOUND) next = current_len - 1; + if (next < cur + *c) { + *c = next - cur; + } + cur += *c; + } + return(cur); +} + +void add_hist(CORD s) +{ + history new_file = GC_NEW(struct HistoryRep); + + new_file -> file_contents = current = s; + current_len = CORD_len(s); + new_file -> previous = now; + if (now != 0) now -> map = current_map; + now = new_file; +} + +void del_hist(void) +{ + now = now -> previous; + current = now -> file_contents; + current_map = now -> map; + current_len = CORD_len(current); +} + +/* Current screen_contents; a dynamically allocated array of CORDs */ +CORD * screen = 0; +int screen_size = 0; + +# ifndef WIN32 +/* Replace a line in the curses stdscr. All control characters are */ +/* displayed as upper case characters in standout mode. This isn't */ +/* terribly appropriate for tabs. */ +void replace_line(int i, CORD s) +{ + register int c; + CORD_pos p; + size_t len = CORD_len(s); + + if (screen == 0 || LINES > screen_size) { + screen_size = LINES; + screen = (CORD *)GC_MALLOC(screen_size * sizeof(CORD)); + } +# if !defined(MACINTOSH) + /* A gross workaround for an apparent curses bug: */ + if (i == LINES-1 && len == COLS) { + s = CORD_substr(s, 0, CORD_len(s) - 1); + } +# endif + if (CORD_cmp(screen[i], s) != 0) { + move(i, 0); clrtoeol(); move(i,0); + + CORD_FOR (p, s) { + c = CORD_pos_fetch(p) & 0x7f; + if (iscntrl(c)) { + standout(); addch(c + 0x40); standend(); + } else { + addch(c); + } + } + screen[i] = s; + } +} +#else +# define replace_line(i,s) invalidate_line(i) +#endif + +/* Return up to COLS characters of the line of s starting at pos, */ +/* returning only characters after the given column. */ +CORD retrieve_line(CORD s, size_t pos, unsigned column) +{ + CORD candidate = CORD_substr(s, pos, column + COLS); + /* avoids scanning very long lines */ + int eol = CORD_chr(candidate, 0, '\n'); + int len; + + if (eol == CORD_NOT_FOUND) eol = CORD_len(candidate); + len = (int)eol - (int)column; + if (len < 0) len = 0; + return(CORD_substr(s, pos + column, len)); +} + +# ifdef WIN32 +# define refresh(); + + CORD retrieve_screen_line(int i) + { + register size_t pos; + + invalidate_map(dis_line + LINES); /* Prune search */ + pos = line_pos(dis_line + i, 0); + if (pos == CORD_NOT_FOUND) return(CORD_EMPTY); + return(retrieve_line(current, pos, dis_col)); + } +# endif + +/* Display the visible section of the current file */ +void redisplay(void) +{ + register int i; + + invalidate_map(dis_line + LINES); /* Prune search */ + for (i = 0; i < LINES; i++) { + if (need_redisplay == ALL || need_redisplay == i) { + register size_t pos = line_pos(dis_line + i, 0); + + if (pos == CORD_NOT_FOUND) break; + replace_line(i, retrieve_line(current, pos, dis_col)); + if (need_redisplay == i) goto done; + } + } + for (; i < LINES; i++) replace_line(i, CORD_EMPTY); +done: + refresh(); + need_redisplay = NONE; +} + +int dis_granularity; + +/* Update dis_line, dis_col, and dis_pos to make cursor visible. */ +/* Assumes line, col, dis_line, dis_pos are in bounds. */ +void normalize_display() +{ + int old_line = dis_line; + int old_col = dis_col; + + dis_granularity = 1; + if (LINES > 15 && COLS > 15) dis_granularity = 2; + while (dis_line > line) dis_line -= dis_granularity; + while (dis_col > col) dis_col -= dis_granularity; + while (line >= dis_line + LINES) dis_line += dis_granularity; + while (col >= dis_col + COLS) dis_col += dis_granularity; + if (old_line != dis_line || old_col != dis_col) { + need_redisplay = ALL; + } +} + +# if defined(WIN32) +# elif defined(MACINTOSH) +# define move_cursor(x,y) cgotoxy(x + 1, y + 1, stdout) +# else +# define move_cursor(x,y) move(y,x) +# endif + +/* Adjust display so that cursor is visible; move cursor into position */ +/* Update screen if necessary. */ +void fix_cursor(void) +{ + normalize_display(); + if (need_redisplay != NONE) redisplay(); + move_cursor(col - dis_col, line - dis_line); + refresh(); +# ifndef WIN32 + fflush(stdout); +# endif +} + +/* Make sure line, col, and dis_pos are somewhere inside file. */ +/* Recompute file_pos. Assumes dis_pos is accurate or past eof */ +void fix_pos() +{ + int my_col = col; + + if ((size_t)line > current_len) line = current_len; + file_pos = line_pos(line, &my_col); + if (file_pos == CORD_NOT_FOUND) { + for (line = current_map -> line, file_pos = current_map -> pos; + file_pos < current_len; + line++, file_pos = CORD_chr(current, file_pos, '\n') + 1); + line--; + file_pos = line_pos(line, &col); + } else { + col = my_col; + } +} + +#if defined(WIN32) +# define beep() Beep(1000 /* Hz */, 300 /* msecs */) +#elif defined(MACINTOSH) +# define beep() SysBeep(1) +#else +/* + * beep() is part of some curses packages and not others. + * We try to match the type of the builtin one, if any. + */ +#ifdef __STDC__ + int beep(void) +#else + int beep() +#endif +{ + putc('\007', stderr); + return(0); +} +#endif + +# define NO_PREFIX -1 +# define BARE_PREFIX -2 +int repeat_count = NO_PREFIX; /* Current command prefix. */ + +int locate_mode = 0; /* Currently between 2 ^Ls */ +CORD locate_string = CORD_EMPTY; /* Current search string. */ + +char * arg_file_name; + +#ifdef WIN32 +/* Change the current position to whatever is currently displayed at */ +/* the given SCREEN coordinates. */ +void set_position(int c, int l) +{ + line = l + dis_line; + col = c + dis_col; + fix_pos(); + move_cursor(col - dis_col, line - dis_line); +} +#endif /* WIN32 */ + +/* Perform the command associated with character c. C may be an */ +/* integer > 256 denoting a windows command, one of the above control */ +/* characters, or another ASCII character to be used as either a */ +/* character to be inserted, a repeat count, or a search string, */ +/* depending on the current state. */ +void do_command(int c) +{ + int i; + int need_fix_pos; + FILE * out; + + if ( c == '\r') c = '\n'; + if (locate_mode) { + size_t new_pos; + + if (c == LOCATE) { + locate_mode = 0; + locate_string = CORD_EMPTY; + return; + } + locate_string = CORD_cat_char(locate_string, (char)c); + new_pos = CORD_str(current, file_pos - CORD_len(locate_string) + 1, + locate_string); + if (new_pos != CORD_NOT_FOUND) { + need_redisplay = ALL; + new_pos += CORD_len(locate_string); + for (;;) { + file_pos = line_pos(line + 1, 0); + if (file_pos > new_pos) break; + line++; + } + col = new_pos - line_pos(line, 0); + file_pos = new_pos; + fix_cursor(); + } else { + locate_string = CORD_substr(locate_string, 0, + CORD_len(locate_string) - 1); + beep(); + } + return; + } + if (c == REPEAT) { + repeat_count = BARE_PREFIX; return; + } else if (c < 0x100 && isdigit(c)){ + if (repeat_count == BARE_PREFIX) { + repeat_count = c - '0'; return; + } else if (repeat_count != NO_PREFIX) { + repeat_count = 10 * repeat_count + c - '0'; return; + } + } + if (repeat_count == NO_PREFIX) repeat_count = 1; + if (repeat_count == BARE_PREFIX && (c == UP || c == DOWN)) { + repeat_count = LINES - dis_granularity; + } + if (repeat_count == BARE_PREFIX) repeat_count = 8; + need_fix_pos = 0; + for (i = 0; i < repeat_count; i++) { + switch(c) { + case LOCATE: + locate_mode = 1; + break; + case TOP: + line = col = file_pos = 0; + break; + case UP: + if (line != 0) { + line--; + need_fix_pos = 1; + } + break; + case DOWN: + line++; + need_fix_pos = 1; + break; + case LEFT: + if (col != 0) { + col--; file_pos--; + } + break; + case RIGHT: + if (CORD_fetch(current, file_pos) == '\n') break; + col++; file_pos++; + break; + case UNDO: + del_hist(); + need_redisplay = ALL; need_fix_pos = 1; + break; + case BS: + if (col == 0) { + beep(); + break; + } + col--; file_pos--; + /* fall through: */ + case DEL: + if (file_pos == current_len-1) break; + /* Can't delete trailing newline */ + if (CORD_fetch(current, file_pos) == '\n') { + need_redisplay = ALL; need_fix_pos = 1; + } else { + need_redisplay = line - dis_line; + } + add_hist(CORD_cat( + CORD_substr(current, 0, file_pos), + CORD_substr(current, file_pos+1, current_len))); + invalidate_map(line); + break; + case WRITE: + { + CORD name = CORD_cat(CORD_from_char_star(arg_file_name), + ".new"); + + if ((out = fopen(CORD_to_const_char_star(name), "wb")) == NULL + || CORD_put(current, out) == EOF) { + de_error("Write failed\n"); + need_redisplay = ALL; + } else { + fclose(out); + } + } + break; + default: + { + CORD left_part = CORD_substr(current, 0, file_pos); + CORD right_part = CORD_substr(current, file_pos, current_len); + + add_hist(CORD_cat(CORD_cat_char(left_part, (char)c), + right_part)); + invalidate_map(line); + if (c == '\n') { + col = 0; line++; file_pos++; + need_redisplay = ALL; + } else { + col++; file_pos++; + need_redisplay = line - dis_line; + } + break; + } + } + } + if (need_fix_pos) fix_pos(); + fix_cursor(); + repeat_count = NO_PREFIX; +} + +/* OS independent initialization */ + +void generic_init(void) +{ + FILE * f; + CORD initial; + + if ((f = fopen(arg_file_name, "rb")) == NULL) { + initial = "\n"; + } else { + initial = CORD_from_file(f); + if (initial == CORD_EMPTY + || CORD_fetch(initial, CORD_len(initial)-1) != '\n') { + initial = CORD_cat(initial, "\n"); + } + } + add_map(0,0); + add_hist(initial); + now -> map = current_map; + now -> previous = now; /* Can't back up further: beginning of the world */ + need_redisplay = ALL; + fix_cursor(); +} + +#ifndef WIN32 + +main(argc, argv) +int argc; +char ** argv; +{ + int c; + +#if defined(MACINTOSH) + console_options.title = "\pDumb Editor"; + cshow(stdout); + GC_init(); + argc = ccommand(&argv); +#endif + + if (argc != 2) goto usage; + arg_file_name = argv[1]; + setvbuf(stdout, GC_MALLOC_ATOMIC(8192), _IOFBF, 8192); + initscr(); + noecho(); nonl(); cbreak(); + generic_init(); + while ((c = getchar()) != QUIT) { + if (c == EOF) break; + do_command(c); + } +done: + move(LINES-1, 0); + clrtoeol(); + refresh(); + nl(); + echo(); + endwin(); + exit(0); +usage: + fprintf(stderr, "Usage: %s file\n", argv[0]); + fprintf(stderr, "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n"); + fprintf(stderr, "Undo: ^U Write to <file>.new: ^W"); + fprintf(stderr, "Quit:^D Repeat count: ^R[n]\n"); + fprintf(stderr, "Top: ^T Locate (search, find): ^L text ^L\n"); + exit(1); +} + +#endif /* !WIN32 */ diff --git a/gc/cord/de_cmds.h b/gc/cord/de_cmds.h new file mode 100644 index 0000000..f42ddcf --- /dev/null +++ b/gc/cord/de_cmds.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:24 pm PDT */ + +#ifndef DE_CMDS_H + +# define DE_CMDS_H + +# define UP 16 /* ^P */ +# define DOWN 14 /* ^N */ +# define LEFT 2 /* ^B */ +# define RIGHT 6 /* ^F */ +# define DEL 127 /* ^? */ +# define BS 8 /* ^H */ +# define UNDO 21 /* ^U */ +# define WRITE 23 /* ^W */ +# define QUIT 4 /* ^D */ +# define REPEAT 18 /* ^R */ +# define LOCATE 12 /* ^L */ +# define TOP 20 /* ^T */ + +#endif + diff --git a/gc/cord/de_win.ICO b/gc/cord/de_win.ICO Binary files differnew file mode 100755 index 0000000..b20ac3e --- /dev/null +++ b/gc/cord/de_win.ICO diff --git a/gc/cord/de_win.RC b/gc/cord/de_win.RC new file mode 100644 index 0000000..554a300 --- /dev/null +++ b/gc/cord/de_win.RC @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to copy this garbage collector for any purpose, + * provided the above notices are retained on all copies. + */ +/* Boehm, May 13, 1994 9:50 am PDT */ + +#include "windows.h" +#include "de_cmds.h" +#include "de_win.h" + + + +ABOUTBOX DIALOG 19, 21, 163, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About Demonstration Text Editor" +BEGIN + ICON "DE", -1, 8, 8, 13, 13, WS_CHILD | WS_VISIBLE + LTEXT "Demonstration Text Editor", -1, 44, 8, 118, 8, WS_CHILD | WS_VISIBLE | WS_GROUP + LTEXT "Version 4.1", -1, 44, 16, 60, 8, WS_CHILD | WS_VISIBLE | WS_GROUP + PUSHBUTTON "OK", IDOK, 118, 27, 24, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +END + + +DE MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Save\t^W", IDM_FILESAVE + MENUITEM "E&xit\t^D", IDM_FILEEXIT + END + + POPUP "&Edit" + BEGIN + MENUITEM "Page &Down\t^R^N", IDM_EDITPDOWN + MENUITEM "Page &Up\t^R^P", IDM_EDITPUP + MENUITEM "U&ndo\t^U", IDM_EDITUNDO + MENUITEM "&Locate\t^L ... ^L", IDM_EDITLOCATE + MENUITEM "D&own\t^N", IDM_EDITDOWN + MENUITEM "U&p\t^P", IDM_EDITUP + MENUITEM "Le&ft\t^B", IDM_EDITLEFT + MENUITEM "&Right\t^F", IDM_EDITRIGHT + MENUITEM "Delete &Backward\tBS", IDM_EDITBS + MENUITEM "Delete F&orward\tDEL", IDM_EDITDEL + MENUITEM "&Top\t^T", IDM_EDITTOP + END + + POPUP "&Help" + BEGIN + MENUITEM "&Contents", IDM_HELPCONTENTS + MENUITEM "&About...", IDM_HELPABOUT + END + + MENUITEM "Page_&Down", IDM_EDITPDOWN + MENUITEM "Page_&Up", IDM_EDITPUP +END + + +DE ACCELERATORS +BEGIN + "^R", IDM_EDITREPEAT + "^N", IDM_EDITDOWN + "^P", IDM_EDITUP + "^L", IDM_EDITLOCATE + "^B", IDM_EDITLEFT + "^F", IDM_EDITRIGHT + "^T", IDM_EDITTOP + VK_DELETE, IDM_EDITDEL, VIRTKEY + VK_BACK, IDM_EDITBS, VIRTKEY +END + + +DE ICON cord\de_win.ICO + diff --git a/gc/cord/de_win.c b/gc/cord/de_win.c new file mode 100644 index 0000000..fedbfbe --- /dev/null +++ b/gc/cord/de_win.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 6, 1995 12:29 pm PST */ + +/* + * The MS Windows specific part of de. + * This started as the generic Windows application template + * made available by Rob Haack (rhaack@polaris.unm.edu), but + * significant parts didn't survive to the final version. + * + * This was written by a nonexpert windows programmer. + */ + + +#include "windows.h" +#include "gc.h" +#include "cord.h" +#include "de_cmds.h" +#include "de_win.h" + +int LINES = 0; +int COLS = 0; + +char szAppName[] = "DE"; +char FullAppName[] = "Demonstration Editor"; + +HWND hwnd; + +void de_error(char *s) +{ + MessageBox( hwnd, (LPSTR) s, + (LPSTR) FullAppName, + MB_ICONINFORMATION | MB_OK ); + InvalidateRect(hwnd, NULL, TRUE); +} + +int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR command_line, int nCmdShow) +{ + MSG msg; + WNDCLASS wndclass; + HANDLE hAccel; + + if (!hPrevInstance) + { + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = DLGWINDOWEXTRA; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, szAppName); + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); + wndclass.lpszMenuName = "DE"; + wndclass.lpszClassName = szAppName; + + if (RegisterClass (&wndclass) == 0) { + char buf[50]; + + sprintf(buf, "RegisterClass: error code: 0x%X", GetLastError()); + de_error(buf); + return(0); + } + } + + /* Empirically, the command line does not include the command name ... + if (command_line != 0) { + while (isspace(*command_line)) command_line++; + while (*command_line != 0 && !isspace(*command_line)) command_line++; + while (isspace(*command_line)) command_line++; + } */ + + if (command_line == 0 || *command_line == 0) { + de_error("File name argument required"); + return( 0 ); + } else { + char *p = command_line; + + while (*p != 0 && !isspace(*p)) p++; + arg_file_name = CORD_to_char_star( + CORD_substr(command_line, 0, p - command_line)); + } + + hwnd = CreateWindow (szAppName, + FullAppName, + WS_OVERLAPPEDWINDOW | WS_CAPTION, /* Window style */ + CW_USEDEFAULT, 0, /* default pos. */ + CW_USEDEFAULT, 0, /* default width, height */ + NULL, /* No parent */ + NULL, /* Window class menu */ + hInstance, NULL); + if (hwnd == NULL) { + char buf[50]; + + sprintf(buf, "CreateWindow: error code: 0x%X", GetLastError()); + de_error(buf); + return(0); + } + + ShowWindow (hwnd, nCmdShow); + + hAccel = LoadAccelerators( hInstance, szAppName ); + + while (GetMessage (&msg, NULL, 0, 0)) + { + if( !TranslateAccelerator( hwnd, hAccel, &msg ) ) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return msg.wParam; +} + +/* Return the argument with all control characters replaced by blanks. */ +char * plain_chars(char * text, size_t len) +{ + char * result = GC_MALLOC_ATOMIC(len + 1); + register size_t i; + + for (i = 0; i < len; i++) { + if (iscntrl(text[i])) { + result[i] = ' '; + } else { + result[i] = text[i]; + } + } + result[len] = '\0'; + return(result); +} + +/* Return the argument with all non-control-characters replaced by */ +/* blank, and all control characters c replaced by c + 32. */ +char * control_chars(char * text, size_t len) +{ + char * result = GC_MALLOC_ATOMIC(len + 1); + register size_t i; + + for (i = 0; i < len; i++) { + if (iscntrl(text[i])) { + result[i] = text[i] + 0x40; + } else { + result[i] = ' '; + } + } + result[len] = '\0'; + return(result); +} + +int char_width; +int char_height; + +void get_line_rect(int line, int win_width, RECT * rectp) +{ + rectp -> top = line * char_height; + rectp -> bottom = rectp->top + char_height; + rectp -> left = 0; + rectp -> right = win_width; +} + +int caret_visible = 0; /* Caret is currently visible. */ + +int screen_was_painted = 0;/* Screen has been painted at least once. */ + +void update_cursor(void); + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + static FARPROC lpfnAboutBox; + static HANDLE hInstance; + HDC dc; + PAINTSTRUCT ps; + RECT client_area; + RECT this_line; + RECT dummy; + TEXTMETRIC tm; + register int i; + int id; + + switch (message) + { + case WM_CREATE: + hInstance = ( (LPCREATESTRUCT) lParam)->hInstance; + lpfnAboutBox = MakeProcInstance( (FARPROC) AboutBox, hInstance ); + dc = GetDC(hwnd); + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + GetTextMetrics(dc, &tm); + ReleaseDC(hwnd, dc); + char_width = tm.tmAveCharWidth; + char_height = tm.tmHeight + tm.tmExternalLeading; + GetClientRect(hwnd, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + generic_init(); + return(0); + + case WM_CHAR: + if (wParam == QUIT) { + SendMessage( hwnd, WM_CLOSE, 0, 0L ); + } else { + do_command(wParam); + } + return(0); + + case WM_SETFOCUS: + CreateCaret(hwnd, NULL, char_width, char_height); + ShowCaret(hwnd); + caret_visible = 1; + update_cursor(); + return(0); + + case WM_KILLFOCUS: + HideCaret(hwnd); + DestroyCaret(); + caret_visible = 0; + return(0); + + case WM_LBUTTONUP: + { + unsigned xpos = LOWORD(lParam); /* From left */ + unsigned ypos = HIWORD(lParam); /* from top */ + + set_position( xpos/char_width, ypos/char_height ); + return(0); + } + + case WM_COMMAND: + id = LOWORD(wParam); + if (id & EDIT_CMD_FLAG) { + if (id & REPEAT_FLAG) do_command(REPEAT); + do_command(CHAR_CMD(id)); + return( 0 ); + } else { + switch(id) { + case IDM_FILEEXIT: + SendMessage( hwnd, WM_CLOSE, 0, 0L ); + return( 0 ); + + case IDM_HELPABOUT: + if( DialogBox( hInstance, "ABOUTBOX", + hwnd, lpfnAboutBox ) ); + InvalidateRect( hwnd, NULL, TRUE ); + return( 0 ); + case IDM_HELPCONTENTS: + de_error( + "Cursor keys: ^B(left) ^F(right) ^P(up) ^N(down)\n" + "Undo: ^U Write: ^W Quit:^D Repeat count: ^R[n]\n" + "Top: ^T Locate (search, find): ^L text ^L\n"); + return( 0 ); + } + } + break; + + case WM_CLOSE: + DestroyWindow( hwnd ); + return 0; + + case WM_DESTROY: + PostQuitMessage (0); + GC_win32_free_heap(); + return 0; + + case WM_PAINT: + dc = BeginPaint(hwnd, &ps); + GetClientRect(hwnd, &client_area); + COLS = (client_area.right - client_area.left)/char_width; + LINES = (client_area.bottom - client_area.top)/char_height; + SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT)); + for (i = 0; i < LINES; i++) { + get_line_rect(i, client_area.right, &this_line); + if (IntersectRect(&dummy, &this_line, &ps.rcPaint)) { + CORD raw_line = retrieve_screen_line(i); + size_t len = CORD_len(raw_line); + char * text = CORD_to_char_star(raw_line); + /* May contain embedded NULLs */ + char * plain = plain_chars(text, len); + char * blanks = CORD_to_char_star(CORD_chars(' ', + COLS - len)); + char * control = control_chars(text, len); +# define RED RGB(255,0,0) + + SetBkMode(dc, OPAQUE); + SetTextColor(dc, GetSysColor(COLOR_WINDOWTEXT)); + + TextOut(dc, this_line.left, this_line.top, + plain, len); + TextOut(dc, this_line.left + len * char_width, this_line.top, + blanks, COLS - len); + SetBkMode(dc, TRANSPARENT); + SetTextColor(dc, RED); + TextOut(dc, this_line.left, this_line.top, + control, strlen(control)); + } + } + EndPaint(hwnd, &ps); + screen_was_painted = 1; + return 0; + } + return DefWindowProc (hwnd, message, wParam, lParam); +} + +int last_col; +int last_line; + +void move_cursor(int c, int l) +{ + last_col = c; + last_line = l; + + if (caret_visible) update_cursor(); +} + +void update_cursor(void) +{ + SetCaretPos(last_col * char_width, last_line * char_height); + ShowCaret(hwnd); +} + +void invalidate_line(int i) +{ + RECT line; + + if (!screen_was_painted) return; + /* Invalidating a rectangle before painting seems result in a */ + /* major performance problem. */ + get_line_rect(i, COLS*char_width, &line); + InvalidateRect(hwnd, &line, FALSE); +} + +LRESULT CALLBACK AboutBox( HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_INITDIALOG: + SetFocus( GetDlgItem( hDlg, IDOK ) ); + break; + + case WM_COMMAND: + switch( wParam ) + { + case IDOK: + EndDialog( hDlg, TRUE ); + break; + } + break; + + case WM_CLOSE: + EndDialog( hDlg, TRUE ); + return TRUE; + + } + return FALSE; +} + diff --git a/gc/cord/de_win.h b/gc/cord/de_win.h new file mode 100644 index 0000000..57a47b4 --- /dev/null +++ b/gc/cord/de_win.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:25 pm PDT */ + +/* cord.h, de_cmds.h, and windows.h should be included before this. */ + + +# define OTHER_FLAG 0x100 +# define EDIT_CMD_FLAG 0x200 +# define REPEAT_FLAG 0x400 + +# define CHAR_CMD(i) ((i) & 0xff) + +/* MENU: DE */ +#define IDM_FILESAVE (EDIT_CMD_FLAG + WRITE) +#define IDM_FILEEXIT (OTHER_FLAG + 1) +#define IDM_HELPABOUT (OTHER_FLAG + 2) +#define IDM_HELPCONTENTS (OTHER_FLAG + 3) + +#define IDM_EDITPDOWN (REPEAT_FLAG + EDIT_CMD_FLAG + DOWN) +#define IDM_EDITPUP (REPEAT_FLAG + EDIT_CMD_FLAG + UP) +#define IDM_EDITUNDO (EDIT_CMD_FLAG + UNDO) +#define IDM_EDITLOCATE (EDIT_CMD_FLAG + LOCATE) +#define IDM_EDITDOWN (EDIT_CMD_FLAG + DOWN) +#define IDM_EDITUP (EDIT_CMD_FLAG + UP) +#define IDM_EDITLEFT (EDIT_CMD_FLAG + LEFT) +#define IDM_EDITRIGHT (EDIT_CMD_FLAG + RIGHT) +#define IDM_EDITBS (EDIT_CMD_FLAG + BS) +#define IDM_EDITDEL (EDIT_CMD_FLAG + DEL) +#define IDM_EDITREPEAT (EDIT_CMD_FLAG + REPEAT) +#define IDM_EDITTOP (EDIT_CMD_FLAG + TOP) + + + + +/* Windows UI stuff */ + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, + UINT wParam, LONG lParam); + +LRESULT CALLBACK AboutBox( HWND hDlg, UINT message, + UINT wParam, LONG lParam ); + + +/* Screen dimensions. Maintained by de_win.c. */ +extern int LINES; +extern int COLS; + +/* File being edited. */ +extern char * arg_file_name; + +/* Current display position in file. Maintained by de.c */ +extern int dis_line; +extern int dis_col; + +/* Current cursor position in file. */ +extern int line; +extern int col; + +/* + * Calls from de_win.c to de.c + */ + +CORD retrieve_screen_line(int i); + /* Get the contents of i'th screen line. */ + /* Relies on COLS. */ + +void set_position(int x, int y); + /* Set column, row. Upper left of window = (0,0). */ + +void do_command(int); + /* Execute an editor command. */ + /* Agument is a command character or one */ + /* of the IDM_ commands. */ + +void generic_init(void); + /* OS independent initialization */ + + +/* + * Calls from de.c to de_win.c + */ + +void move_cursor(int column, int line); + /* Physically move the cursor on the display, */ + /* so that it appears at */ + /* (column, line). */ + +void invalidate_line(int line); + /* Invalidate line i on the screen. */ + +void de_error(char *s); + /* Display error message. */
\ No newline at end of file diff --git a/gc/cord/ec.h b/gc/cord/ec.h new file mode 100644 index 0000000..c829b83 --- /dev/null +++ b/gc/cord/ec.h @@ -0,0 +1,70 @@ +# ifndef EC_H +# define EC_H + +# ifndef CORD_H +# include "cord.h" +# endif + +/* Extensible cords are strings that may be destructively appended to. */ +/* They allow fast construction of cords from characters that are */ +/* being read from a stream. */ +/* + * A client might look like: + * + * { + * CORD_ec x; + * CORD result; + * char c; + * FILE *f; + * + * ... + * CORD_ec_init(x); + * while(...) { + * c = getc(f); + * ... + * CORD_ec_append(x, c); + * } + * result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +# ifndef CORD_BUFSZ +# define CORD_BUFSZ 128 +# endif + +typedef struct CORD_ec_struct { + CORD ec_cord; + char * ec_bufptr; + char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* This structure represents the concatenation of ec_cord with */ +/* ec_buf[0 ... (ec_bufptr-ec_buf-1)] */ + +/* Flush the buffer part of the extended chord into ec_cord. */ +/* Note that this is almost the only real function, and it is */ +/* implemented in 6 lines in cordxtra.c */ +void CORD_ec_flush_buf(CORD_ec x); + +/* Convert an extensible cord to a cord. */ +# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord) + +/* Initialize an extensible cord. */ +# define CORD_ec_init(x) ((x)[0].ec_cord = 0, (x)[0].ec_bufptr = (x)[0].ec_buf) + +/* Append a character to an extensible cord. */ +# define CORD_ec_append(x, c) \ + { \ + if ((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ) { \ + CORD_ec_flush_buf(x); \ + } \ + *((x)[0].ec_bufptr)++ = (c); \ + } + +/* Append a cord to an extensible cord. Structure remains shared with */ +/* original. */ +void CORD_ec_append_cord(CORD_ec x, CORD s); + +# endif /* EC_H */ diff --git a/gc/cord/gc.h b/gc/cord/gc.h new file mode 100644 index 0000000..3061409 --- /dev/null +++ b/gc/cord/gc.h @@ -0,0 +1,754 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * If you are using Solaris threads, look at the end of this file. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef _GC_H + +# define _GC_H +# define __GC +# include <stddef.h> + +#if defined(__CYGWIN32__) && defined(GC_USE_DLL) +#include "libgc_globals.h" +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif +#endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif +#endif + +#ifndef GC_API +#define GC_API extern +#endif + +# if defined(__STDC__) || defined(__cplusplus) +# define GC_PROTO(args) args + typedef void * GC_PTR; +# else +# define GC_PROTO(args) () + typedef char * GC_PTR; +# endif + +# ifdef __cplusplus + extern "C" { +# endif + + +/* Define word and signed_word to be unsigned and signed types of the */ +/* size as char * or void *. There seems to be no way to do this */ +/* even semi-portably. The following is probably no better/worse */ +/* than almost anything else. */ +/* The ANSI standard suggests that size_t and ptr_diff_t might be */ +/* better choices. But those appear to have incorrect definitions */ +/* on may systems. Notably "typedef int size_t" seems to be both */ +/* frequent and WRONG. */ +typedef unsigned long GC_word; +typedef long GC_signed_word; + +/* Public read-only variables */ + +GC_API GC_word GC_gc_no;/* Counter incremented per collection. */ + /* Includes empty GCs at startup. */ + + +/* Public R/W variables */ + +GC_API GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)); + /* When there is insufficient memory to satisfy */ + /* an allocation request, we return */ + /* (*GC_oom_fn)(). By default this just */ + /* returns 0. */ + /* If it returns, it must return 0 or a valid */ + /* pointer to a previously allocated heap */ + /* object. */ + +GC_API int GC_find_leak; + /* Do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_free. Initial value */ + /* is determined by FIND_LEAK macro. */ + +GC_API int GC_quiet; /* Disable statistics output. Only matters if */ + /* collector has been compiled with statistics */ + /* enabled. This involves a performance cost, */ + /* and is thus not the default. */ + +GC_API int GC_finalize_on_demand; + /* If nonzero, finalizers will only be run in */ + /* response to an eplit GC_invoke_finalizers */ + /* call. The default is determined by whether */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. */ + +GC_API int GC_java_finalization; + /* Mark objects reachable from finalizable */ + /* objects in a separate postpass. This makes */ + /* it a bit safer to use non-topologically- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + +GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */ + /* because it's not safe. */ + +GC_API int GC_dont_expand; + /* Dont expand heap unless explicitly requested */ + /* or forced to. */ + +GC_API int GC_full_freq; /* Number of partial collections between */ + /* full collections. Matters only if */ + /* GC_incremental is set. */ + +GC_API GC_word GC_non_gc_bytes; + /* Bytes not considered candidates for collection. */ + /* Used only to control scheduling of collections. */ + +GC_API GC_word GC_free_space_divisor; + /* We try to make sure that we allocate at */ + /* least N/GC_free_space_divisor bytes between */ + /* collections, where N is the heap size plus */ + /* a rough estimate of the root set size. */ + /* Initially, GC_free_space_divisor = 4. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* GC_free_space_divisor = 1 will effectively */ + /* disable collections. */ + +GC_API GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. Initially 0. */ + + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + +/* Public procedures */ +/* + * general purpose allocation routines, with roughly malloc calling conv. + * The atomic versions promise that no relevant pointers are contained + * in the object. The nonatomic versions guarantee that the new object + * is cleared. GC_malloc_stubborn promises that no changes to the object + * will occur after GC_end_stubborn_change has been called on the + * result of GC_malloc_stubborn. GC_malloc_uncollectable allocates an object + * that is scanned for pointers to collectable objects, but is not itself + * collectable. GC_malloc_uncollectable and GC_free called on the resulting + * object implicitly update GC_non_gc_bytes appropriately. + */ +GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes)); + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes)); + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* If the argument is stubborn, it should not be changeable when freed. */ +/* An object should not be enable for finalization when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_free GC_PROTO((GC_PTR object_addr)); + +/* + * Stubborn objects may be changed only if the collector is explicitly informed. + * The collector is implicitly informed of coming change when such + * an object is first allocated. The following routines inform the + * collector that an object will no longer be changed, or that it will + * once again be changed. Only nonNIL pointer stores into the object + * are considered to be changes. The argument to GC_end_stubborn_change + * must be exacly the value returned by GC_malloc_stubborn or passed to + * GC_change_stubborn. (In the second case it may be an interior pointer + * within 512 bytes of the beginning of the objects.) + * There is a performance penalty for allowing more than + * one stubborn object to be changed at once, but it is acceptable to + * do so. The same applies to dropping stubborn objects that are still + * changeable. + */ +GC_API void GC_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR)); + +/* Return a pointer to the base (lowest address) of an object given */ +/* a pointer to a location within the object. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer)); + +/* Given a pointer to the base of an object, return its size in bytes. */ +/* The returned size may be slightly larger than what was originally */ +/* requested. */ +GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); + +/* For compatibility with C library. This is occasionally faster than */ +/* a malloc followed by a bcopy. But if you rely on that, either here */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* If the argument is stubborn, the result will have changes enabled. */ +/* It is an error to have changes enabled for the original object. */ +/* Follows ANSI comventions for NULL old_object. */ +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); + +/* Explicitly increase the heap size. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes)); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. */ +GC_API void GC_set_max_heap_size GC_PROTO((GC_word n)); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* need not be scanned. This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be */ +/* mistaken for dynamic library data segments on some systems. */ +GC_API void GC_exclude_static_roots GC_PROTO((GC_PTR start, GC_PTR finish)); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_clear_roots GC_PROTO((void)); + +/* Add a root segment. Wizards only. */ +GC_API void GC_add_roots GC_PROTO((char * low_address, + char * high_address_plus_1)); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to n. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector was compiled with recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_register_displacement GC_PROTO((GC_word n)); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_debug_register_displacement GC_PROTO((GC_word n)); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_gcollect GC_PROTO((void)); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. */ +/* Return 0 if the collection was aborted, 1 if it succeeded. */ +typedef int (* GC_stop_func) GC_PROTO((void)); +GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func)); + +/* Return the number of bytes in the heap. Excludes collector private */ +/* data structures. Includes empty blocks and fragmentation loss. */ +/* Includes some pages that were allocated but never written. */ +GC_API size_t GC_get_heap_size GC_PROTO((void)); + +/* Return the number of bytes allocated since the last collection. */ +GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); + +/* Enable incremental/generational collection. */ +/* Not advisable unless dirty bits are */ +/* available or most heap objects are */ +/* pointerfree(atomic) or immutable. */ +/* Don't use in leak finding mode. */ +/* Ignored if GC_dont_gc is true. */ +GC_API void GC_enable_incremental GC_PROTO((void)); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done. */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. */ +GC_API int GC_collect_a_little GC_PROTO((void)); + +/* Allocate an object of size lb bytes. The client guarantees that */ +/* as long as the object is live, it will be referenced by a pointer */ +/* that points to somewhere within the first 256 bytes of the object. */ +/* (This should normally be declared volatile to prevent the compiler */ +/* from invalidating this assertion.) This routine is only useful */ +/* if a large array is being allocated. It reduces the chance of */ +/* accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or MS Windows system this is recommended */ +/* for arrays likely to be larger than 100K or so. For other systems, */ +/* or if the collector is not configured to recognize all interior */ +/* pointers, the threshold is normally much higher. */ +GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); +GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#ifdef GC_ADD_CALLER +# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +# define GC_EXTRA_PARAMS GC_word ra, char * descr_string, int descr_int +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + +/* Debugging (annotated) allocation. GC_gcollect will check */ +/* objects allocated in this way for overwrites, etc. */ +GC_API GC_PTR GC_debug_malloc + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_atomic + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_uncollectable + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_stubborn + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); +GC_API GC_PTR GC_debug_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, + GC_EXTRA_PARAMS)); + +GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); +# ifdef GC_DEBUG +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS); +# define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, GC_base(obj)) +# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +# else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz) +# define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +# endif +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecifed size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. */ +# define GC_NEW(t) (t *)GC_MALLOC(sizeof (t)) +# define GC_NEW_ATOMIC(t) (t *)GC_MALLOC_ATOMIC(sizeof (t)) +# define GC_NEW_STUBBORN(t) (t *)GC_MALLOC_STUBBORN(sizeof (t)) +# define GC_NEW_UNCOLLECTABLE(t) (t *)GC_MALLOC_UNCOLLECTABLE(sizeof (t)) + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (*GC_finalization_proc) + GC_PROTO((GC_PTR obj, GC_PTR client_data)); + +GC_API void GC_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + /* When obj is no longer accessible, invoke */ + /* (*fn)(obj, cd). If a and b are inaccessible, and */ + /* a points to b (after disappearing links have been */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered just before */ + /* they are invoked. */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocation lock is held, and */ + /* such conversions are not performed by finalization */ + /* routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the nonNULL starting address of an */ + /* object allocated by GC_malloc or friends. */ + /* Note that any garbage collectable object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. */ +GC_API void GC_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* where p is a pointer that is not followed by finalization */ +/* code, and should not be considered in determining */ +/* finalization order. */ +GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Link should point to a field of a heap allocated */ + /* object obj. *link will be cleared when obj is */ + /* found to be inaccessible. This happens BEFORE any */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. */ + /* Returns 1 if link was already registered, 0 */ + /* otherwise. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_general_register_disappearing_link + GC_PROTO((GC_PTR * /* link */, GC_PTR obj)); + /* A slight generalization of the above. *link is */ + /* cleared when obj first becomes inaccessible. This */ + /* can be used to implement weak pointers easily and */ + /* safely. Typically link will point to a location */ + /* holding a disguised pointer to obj. (A pointer */ + /* inside an "atomic" object is effectively */ + /* disguised.) In this way soft */ + /* pointers are broken before any object */ + /* reachable from them are finalized. Each link */ + /* May be registered only once, i.e. with one obj */ + /* value. This was added after a long email discussion */ + /* with John Ellis. */ + /* Obj must be a pointer to the first word of an object */ + /* we allocated. It is unsafe to explicitly deallocate */ + /* the object containing link. Explicitly deallocating */ + /* obj may or may not cause link to eventually be */ + /* cleared. */ +GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Returns 0 if link was not actually registered. */ + /* Undoes a registration by either of the above two */ + /* routines. */ + +/* Auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); +GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); + +GC_API int GC_invoke_finalizers GC_PROTO((void)); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC-finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* p may not be a NULL pointer. */ +typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); +GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); + /* Returns old warning procedure. */ + +/* The following is intended to be used by a higher level */ +/* (e.g. cedar-like) finalization facility. It is expected */ +/* that finalization code will arrange for hidden pointers to */ +/* disappear. Otherwise objects can be accessed after they */ +/* have been collected. */ +/* Note that putting pointers in atomic objects or in */ +/* nonpointer slots of "typed" objects is equivalent to */ +/* disguising them in this way, and may have other advantages. */ +# if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) + typedef GC_word GC_hidden_pointer; +# define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +# define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) + /* Converting a hidden pointer to a real pointer requires verifying */ + /* that the object still exists. This involves acquiring the */ + /* allocator lock to avoid a race with the collector. */ +# endif /* I_HIDE_POINTERS */ + +typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); +GC_API GC_PTR GC_call_with_alloc_lock + GC_PROTO((GC_fn_type fn, GC_PTR client_data)); + +/* Check that p and q point to the same object. */ +/* Fail conspicuously if they don't. */ +/* Returns the first argument. */ +/* Succeeds if neither p nor q points to the heap. */ +/* May succeed if both p and q point to between heap objects. */ +GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q)); + +/* Checked pointer pre- and post- increment operations. Note that */ +/* the second argument is in units of bytes, not multiples of the */ +/* object size. This should either be invoked from a macro, or the */ +/* call should be automatically generated. */ +GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much)); +GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much)); + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't fail conspicuously. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for multithreaded worlds. */ +GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p)); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Fail conspicuously if this property does not hold. */ +/* Uninteresting with ALL_INTERIOR_POINTERS. */ +/* Always returns its argument. */ +GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p)); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +#ifdef GC_DEBUG +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr(&(x), (n)*sizeof(*x)) +# define GC_POST_INCR2(x, type_of_result) \ + ((type_of_result)GC_post_incr(&(x), sizeof(*x)) +# ifdef __GNUC__ +# define GC_PTR_ADD(x, n) \ + GC_PTR_ADD3(x, n, typeof(x)) +# define GC_PRE_INCR(x, n) \ + GC_PRE_INCR3(x, n, typeof(x)) +# define GC_POST_INCR(x, n) \ + GC_POST_INCR3(x, typeof(x)) +# else + /* We can't do this right without typeof, which ANSI */ + /* decided was not sufficiently useful. Repeatedly */ + /* mentioning the arguments seems too dangerous to be */ + /* useful. So does not casting the result. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_ADD3(x, n, type_of_result) ((x)+(n)) +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR3(x, n, type_of_result) ((x) += (n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR2(x, n, type_of_result) ((x)++) +# define GC_POST_INCR(x, n) ((x)++) +#endif + +/* Safer assignment of a pointer to a nonstack location. */ +#ifdef GC_DEBUG +# ifdef __STDC__ +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# else +# define GC_PTR_STORE(p, q) \ + (*(char **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_STORE(p, q) *((p) = (q)) +#endif + +/* Fynctions called to report pointer checking errors */ +GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q)); + +GC_API void (*GC_is_valid_displacement_print_proc) + GC_PROTO((GC_PTR p)); + +GC_API void (*GC_is_visible_print_proc) + GC_PROTO((GC_PTR p)); + +#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +#endif + +#ifdef SOLARIS_THREADS +/* We need to intercept calls to many of the threads primitives, so */ +/* that we can locate thread stacks and stop the world. */ +/* Note also that the collector cannot see thread specific data. */ +/* Thread specific data should generally consist of pointers to */ +/* uncollectable objects, which are deallocated using the destructor */ +/* facility in thr_keycreate. */ +# include <thread.h> +# include <signal.h> + int GC_thr_create(void *stack_base, size_t stack_size, + void *(*start_routine)(void *), void *arg, long flags, + thread_t *new_thread); + int GC_thr_join(thread_t wait_for, thread_t *departed, void **status); + int GC_thr_suspend(thread_t target_thread); + int GC_thr_continue(thread_t target_thread); + void * GC_dlopen(const char *path, int mode); + +# ifdef _SOLARIS_PTHREADS +# include <pthread.h> + extern int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void * (*thread_execp)(void *), void *arg); + extern int GC_pthread_join(pthread_t wait_for, void **status); + +# undef thread_t + +# define pthread_join GC_pthread_join +# define pthread_create GC_pthread_create +#endif + +# define thr_create GC_thr_create +# define thr_join GC_thr_join +# define thr_suspend GC_thr_suspend +# define thr_continue GC_thr_continue +# define dlopen GC_dlopen + +# endif /* SOLARIS_THREADS */ + + +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) +/* We treat these similarly. */ +# include <pthread.h> +# include <signal.h> + + int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); + int GC_pthread_join(pthread_t thread, void **retval); + +# define pthread_create GC_pthread_create +# define pthread_sigmask GC_pthread_sigmask +# define pthread_join GC_pthread_join + +#endif /* IRIX_THREADS || LINUX_THREADS */ + +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) + /* Any flavor of threads except SRC_M3. */ +/* This returns a list of objects, linked through their first */ +/* word. Its use can greatly reduce lock contention problems, since */ +/* the allocation lock can be acquired and released many fewer times. */ +/* lb must be large enough to hold the pointer field. */ +GC_PTR GC_malloc_many(size_t lb); +#define GC_NEXT(p) (*(GC_PTR *)(p)) /* Retrieve the next element */ + /* in returned list. */ +extern void GC_thr_init(); /* Needed for Solaris/X86 */ + +#endif /* THREADS && !SRC_M3 */ + +/* + * If you are planning on putting + * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() + * from the statically loaded program section. + * This circumvents a Solaris 2.X (X<=4) linker bug. + */ +#if defined(sparc) || defined(__sparc) +# define GC_INIT() { extern end, etext; \ + GC_noop(&end, &etext); } +#else +# if defined(__CYGWIN32__) && defined(GC_USE_DLL) + /* + * Similarly gnu-win32 DLLs need explicit initialization + */ +# define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } +# else +# define GC_INIT() +# endif +#endif + +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); +#endif + +#ifdef __cplusplus + } /* end of extern "C" */ +#endif + +#endif /* _GC_H */ diff --git a/gc/cord/private/cord_pos.h b/gc/cord/private/cord_pos.h new file mode 100644 index 0000000..d2b24bb --- /dev/null +++ b/gc/cord/private/cord_pos.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:23 pm PDT */ +# ifndef CORD_POSITION_H + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +# define MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We don't let cords get deeper than MAX_DEPTH. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + int path_len; +# define CORD_POS_INVALID (0x55555555) + /* path_len == INVALID <==> position invalid */ + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf */ + size_t cur_end; /* Ending position of cur_leaf */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define FUNCTION_BUF_SZ 8 + char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position: */ +CORD CORD_pos_to_cord(CORD_pos p); + +/* Extract the current index from a position: */ +size_t CORD_pos_to_index(CORD_pos p); + +/* Fetch the character located at the given position: */ +char CORD_pos_fetch(CORD_pos p); + +/* Initialize the position to refer to the give cord and index. */ +/* Note that this is the most expensive function on positions: */ +void CORD_set_pos(CORD_pos p, CORD x, size_t i); + +/* Advance the position to the next character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past end: */ +void CORD_next(CORD_pos p); + +/* Move the position to the preceding character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past beginning: */ +void CORD_prev(CORD_pos p); + +/* Is the position valid, i.e. inside the cord? */ +int CORD_pos_valid(CORD_pos p); + +char CORD__pos_fetch(CORD_pos); +void CORD__next(CORD_pos); +void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + (((p)[0].cur_end != 0)? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0)) + +#define CORD_prev(p) \ + (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos)) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters */ + /* 0 < n < CORD_pos_chars_left(p) */ + +#define CORD_pos_cur_char_addr(p) \ + (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + /* address of current character in cache. */ + +#endif diff --git a/gc/dbg_mlc.c b/gc/dbg_mlc.c new file mode 100644 index 0000000..6483256 --- /dev/null +++ b/gc/dbg_mlc.c @@ -0,0 +1,799 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +# define I_HIDE_POINTERS +# include "gc_priv.h" +# ifdef KEEP_BACK_PTRS +# include "backptr.h" +# endif + +void GC_default_print_heap_obj_proc(); +GC_API void GC_register_finalizer_no_order + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + +/* Do we want to and know how to save the call stack at the time of */ +/* an allocation? How much space do we want to use in each object? */ + +# define START_FLAG ((word)0xfedcedcb) +# define END_FLAG ((word)0xbcdecdef) + /* Stored both one past the end of user object, and one before */ + /* the end of the object as seen by the allocator. */ + + +/* Object header */ +typedef struct { +# ifdef KEEP_BACK_PTRS + ptr_t oh_back_ptr; +# define MARKED_FOR_FINALIZATION (ptr_t)(-1) + /* Object was marked because it is finalizable. */ +# ifdef ALIGN_DOUBLE + word oh_dummy; +# endif +# endif + char * oh_string; /* object descriptor string */ + word oh_int; /* object descriptor integers */ +# ifdef NEED_CALLINFO + struct callinfo oh_ci[NFRAMES]; +# endif + word oh_sz; /* Original malloc arg. */ + word oh_sf; /* start flag */ +} oh; +/* The size of the above structure is assumed not to dealign things, */ +/* and to be a multiple of the word length. */ + +#define DEBUG_BYTES (sizeof (oh) + sizeof (word)) +#undef ROUNDED_UP_WORDS +#define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1) + + +#ifdef SAVE_CALL_CHAIN +# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +#else +# ifdef GC_ADD_CALLER +# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra) +# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci) +# else +# define ADD_CALL_CHAIN(base, ra) +# define PRINT_CALL_CHAIN(base) +# endif +#endif + +/* Check whether object with base pointer p has debugging info */ +/* p is assumed to point to a legitimate object in our part */ +/* of the heap. */ +GC_bool GC_has_debug_info(p) +ptr_t p; +{ + register oh * ohdr = (oh *)p; + register ptr_t body = (ptr_t)(ohdr + 1); + register word sz = GC_size((ptr_t) ohdr); + + if (HBLKPTR((ptr_t)ohdr) != HBLKPTR((ptr_t)body) + || sz < sizeof (oh)) { + return(FALSE); + } + if (ohdr -> oh_sz == sz) { + /* Object may have had debug info, but has been deallocated */ + return(FALSE); + } + if (ohdr -> oh_sf == (START_FLAG ^ (word)body)) return(TRUE); + if (((word *)ohdr)[BYTES_TO_WORDS(sz)-1] == (END_FLAG ^ (word)body)) { + return(TRUE); + } + return(FALSE); +} + +#ifdef KEEP_BACK_PTRS + /* Store back pointer to source in dest, if that appears to be possible. */ + /* This is not completely safe, since we may mistakenly conclude that */ + /* dest has a debugging wrapper. But the error probability is very */ + /* small, and this shouldn't be used in production code. */ + /* We assume that dest is the real base pointer. Source will usually */ + /* be a pointer to the interior of an object. */ + void GC_store_back_pointer(ptr_t source, ptr_t dest) + { + if (GC_has_debug_info(dest)) { + ((oh *)dest) -> oh_back_ptr = (ptr_t)HIDE_POINTER(source); + } + } + + void GC_marked_for_finalization(ptr_t dest) { + GC_store_back_pointer(MARKED_FOR_FINALIZATION, dest); + } + + /* Store information about the object referencing dest in *base_p */ + /* and *offset_p. */ + /* source is root ==> *base_p = 0, *offset_p = address */ + /* source is heap object ==> *base_p != 0, *offset_p = offset */ + /* Returns 1 on success, 0 if source couldn't be determined. */ + /* Dest can be any address within a heap object. */ + GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p) + { + oh * hdr = (oh *)GC_base(dest); + ptr_t bp; + ptr_t bp_base; + if (!GC_has_debug_info((ptr_t) hdr)) return GC_NO_SPACE; + bp = hdr -> oh_back_ptr; + if (MARKED_FOR_FINALIZATION == bp) return GC_FINALIZER_REFD; + if (0 == bp) return GC_UNREFERENCED; + bp = REVEAL_POINTER(bp); + bp_base = GC_base(bp); + if (0 == bp_base) { + *base_p = bp; + *offset_p = 0; + return GC_REFD_FROM_ROOT; + } else { + if (GC_has_debug_info(bp_base)) bp_base += sizeof(oh); + *base_p = bp_base; + *offset_p = bp - bp_base; + return GC_REFD_FROM_HEAP; + } + } + + /* Generate a random heap address. */ + /* The resulting address is in the heap, but */ + /* not necessarily inside a valid object. */ + void *GC_generate_random_heap_address(void) + { + int i; + int heap_offset = random() % GC_heapsize; + for (i = 0; i < GC_n_heap_sects; ++ i) { + int size = GC_heap_sects[i].hs_bytes; + if (heap_offset < size) { + return GC_heap_sects[i].hs_start + heap_offset; + } else { + heap_offset -= size; + } + } + ABORT("GC_generate_random_heap_address: size inconsistency"); + /*NOTREACHED*/ + return 0; + } + + /* Generate a random address inside a valid marked heap object. */ + void *GC_generate_random_valid_address(void) + { + ptr_t result; + ptr_t base; + for (;;) { + result = GC_generate_random_heap_address(); + base = GC_base(result); + if (0 == base) continue; + if (!GC_is_marked(base)) continue; + return result; + } + } + + /* Force a garbage collection and generate a backtrace from a */ + /* random heap address. */ + void GC_generate_random_backtrace(void) + { + void * current; + int i; + void * base; + size_t offset; + GC_ref_kind source; + GC_gcollect(); + current = GC_generate_random_valid_address(); + GC_printf1("Chose address 0x%lx in object\n", (unsigned long)current); + GC_print_heap_obj(GC_base(current)); + GC_err_printf0("\n"); + for (i = 0; ; ++i) { + source = GC_get_back_ptr_info(current, &base, &offset); + if (GC_UNREFERENCED == source) { + GC_err_printf0("Reference could not be found\n"); + goto out; + } + if (GC_NO_SPACE == source) { + GC_err_printf0("No debug info in object: Can't find reference\n"); + goto out; + } + GC_err_printf1("Reachable via %d levels of pointers from ", + (unsigned long)i); + switch(source) { + case GC_REFD_FROM_ROOT: + GC_err_printf1("root at 0x%lx\n", (unsigned long)base); + goto out; + case GC_FINALIZER_REFD: + GC_err_printf0("list of finalizable objects\n"); + goto out; + case GC_REFD_FROM_HEAP: + GC_err_printf1("offset %ld in object:\n", (unsigned long)offset); + /* Take GC_base(base) to get real base, i.e. header. */ + GC_print_heap_obj(GC_base(base)); + GC_err_printf0("\n"); + break; + } + current = base; + } + out:; + } + +#endif /* KEEP_BACK_PTRS */ + +/* Store debugging info into p. Return displaced pointer. */ +/* Assumes we don't hold allocation lock. */ +ptr_t GC_store_debug_info(p, sz, string, integer) +register ptr_t p; /* base pointer */ +word sz; /* bytes */ +char * string; +word integer; +{ + register word * result = (word *)((oh *)p + 1); + DCL_LOCK_STATE; + + /* There is some argument that we should dissble signals here. */ + /* But that's expensive. And this way things should only appear */ + /* inconsistent while we're in the handler. */ + LOCK(); +# ifdef KEEP_BACK_PTRS + ((oh *)p) -> oh_back_ptr = 0; +# endif + ((oh *)p) -> oh_string = string; + ((oh *)p) -> oh_int = integer; + ((oh *)p) -> oh_sz = sz; + ((oh *)p) -> oh_sf = START_FLAG ^ (word)result; + ((word *)p)[BYTES_TO_WORDS(GC_size(p))-1] = + result[ROUNDED_UP_WORDS(sz)] = END_FLAG ^ (word)result; + UNLOCK(); + return((ptr_t)result); +} + +/* Check the object with debugging info at ohdr */ +/* return NIL if it's OK. Else return clobbered */ +/* address. */ +ptr_t GC_check_annotated_obj(ohdr) +register oh * ohdr; +{ + register ptr_t body = (ptr_t)(ohdr + 1); + register word gc_sz = GC_size((ptr_t)ohdr); + if (ohdr -> oh_sz + DEBUG_BYTES > gc_sz) { + return((ptr_t)(&(ohdr -> oh_sz))); + } + if (ohdr -> oh_sf != (START_FLAG ^ (word)body)) { + return((ptr_t)(&(ohdr -> oh_sf))); + } + if (((word *)ohdr)[BYTES_TO_WORDS(gc_sz)-1] != (END_FLAG ^ (word)body)) { + return((ptr_t)((word *)ohdr + BYTES_TO_WORDS(gc_sz)-1)); + } + if (((word *)body)[ROUNDED_UP_WORDS(ohdr -> oh_sz)] + != (END_FLAG ^ (word)body)) { + return((ptr_t)((word *)body + ROUNDED_UP_WORDS(ohdr -> oh_sz))); + } + return(0); +} + +void GC_print_obj(p) +ptr_t p; +{ + register oh * ohdr = (oh *)GC_base(p); + + GC_err_printf1("0x%lx (", ((unsigned long)ohdr + sizeof(oh))); + GC_err_puts(ohdr -> oh_string); + GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int), + (unsigned long)(ohdr -> oh_sz)); + PRINT_CALL_CHAIN(ohdr); +} + +void GC_debug_print_heap_obj_proc(p) +ptr_t p; +{ + if (GC_has_debug_info(p)) { + GC_print_obj(p); + } else { + GC_default_print_heap_obj_proc(p); + } +} + +void GC_print_smashed_obj(p, clobbered_addr) +ptr_t p, clobbered_addr; +{ + register oh * ohdr = (oh *)GC_base(p); + + GC_err_printf2("0x%lx in object at 0x%lx(", (unsigned long)clobbered_addr, + (unsigned long)p); + if (clobbered_addr <= (ptr_t)(&(ohdr -> oh_sz)) + || ohdr -> oh_string == 0) { + GC_err_printf1("<smashed>, appr. sz = %ld)\n", + (GC_size((ptr_t)ohdr) - DEBUG_BYTES)); + } else { + if (ohdr -> oh_string[0] == '\0') { + GC_err_puts("EMPTY(smashed?)"); + } else { + GC_err_puts(ohdr -> oh_string); + } + GC_err_printf2(":%ld, sz=%ld)\n", (unsigned long)(ohdr -> oh_int), + (unsigned long)(ohdr -> oh_sz)); + PRINT_CALL_CHAIN(ohdr); + } +} + +void GC_check_heap_proc(); + +void GC_start_debugging() +{ + GC_check_heap = GC_check_heap_proc; + GC_print_heap_obj = GC_debug_print_heap_obj_proc; + GC_debugging_started = TRUE; + GC_register_displacement((word)sizeof(oh)); +} + +# if defined(__STDC__) || defined(__cplusplus) + void GC_debug_register_displacement(GC_word offset) +# else + void GC_debug_register_displacement(offset) + GC_word offset; +# endif +{ + GC_register_displacement(offset); + GC_register_displacement((word)sizeof(oh) + offset); +} + +# ifdef GC_ADD_CALLER +# define EXTRA_ARGS word ra, char * s, int i +# define OPT_RA ra, +# else +# define EXTRA_ARGS char * s, int i +# define OPT_RA +# endif + +# ifdef __STDC__ + GC_PTR GC_debug_malloc(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc(lb, s, i) + size_t lb; + char * s; + int i; +# ifdef GC_ADD_CALLER + --> GC_ADD_CALLER not implemented for K&R C +# endif +# endif +{ + GC_PTR result = GC_malloc(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + +#ifdef STUBBORN_ALLOC +# ifdef __STDC__ + GC_PTR GC_debug_malloc_stubborn(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc_stubborn(lb, s, i) + size_t lb; + char * s; + int i; +# endif +{ + GC_PTR result = GC_malloc_stubborn(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + +void GC_debug_change_stubborn(p) +GC_PTR p; +{ + register GC_PTR q = GC_base(p); + register hdr * hhdr; + + if (q == 0) { + GC_err_printf1("Bad argument: 0x%lx to GC_debug_change_stubborn\n", + (unsigned long) p); + ABORT("GC_debug_change_stubborn: bad arg"); + } + hhdr = HDR(q); + if (hhdr -> hb_obj_kind != STUBBORN) { + GC_err_printf1("GC_debug_change_stubborn arg not stubborn: 0x%lx\n", + (unsigned long) p); + ABORT("GC_debug_change_stubborn: arg not stubborn"); + } + GC_change_stubborn(q); +} + +void GC_debug_end_stubborn_change(p) +GC_PTR p; +{ + register GC_PTR q = GC_base(p); + register hdr * hhdr; + + if (q == 0) { + GC_err_printf1("Bad argument: 0x%lx to GC_debug_end_stubborn_change\n", + (unsigned long) p); + ABORT("GC_debug_end_stubborn_change: bad arg"); + } + hhdr = HDR(q); + if (hhdr -> hb_obj_kind != STUBBORN) { + GC_err_printf1("debug_end_stubborn_change arg not stubborn: 0x%lx\n", + (unsigned long) p); + ABORT("GC_debug_end_stubborn_change: arg not stubborn"); + } + GC_end_stubborn_change(q); +} + +#endif /* STUBBORN_ALLOC */ + +# ifdef __STDC__ + GC_PTR GC_debug_malloc_atomic(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc_atomic(lb, s, i) + size_t lb; + char * s; + int i; +# endif +{ + GC_PTR result = GC_malloc_atomic(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc_atomic(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + +# ifdef __STDC__ + GC_PTR GC_debug_malloc_uncollectable(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc_uncollectable(lb, s, i) + size_t lb; + char * s; + int i; +# endif +{ + GC_PTR result = GC_malloc_uncollectable(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1("GC_debug_malloc_uncollectable(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} + +#ifdef ATOMIC_UNCOLLECTABLE +# ifdef __STDC__ + GC_PTR GC_debug_malloc_atomic_uncollectable(size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_malloc_atomic_uncollectable(lb, s, i) + size_t lb; + char * s; + int i; +# endif +{ + GC_PTR result = GC_malloc_atomic_uncollectable(lb + DEBUG_BYTES); + + if (result == 0) { + GC_err_printf1( + "GC_debug_malloc_atomic_uncollectable(%ld) returning NIL (", + (unsigned long) lb); + GC_err_puts(s); + GC_err_printf1(":%ld)\n", (unsigned long)i); + return(0); + } + if (!GC_debugging_started) { + GC_start_debugging(); + } + ADD_CALL_CHAIN(result, ra); + return (GC_store_debug_info(result, (word)lb, s, (word)i)); +} +#endif /* ATOMIC_UNCOLLECTABLE */ + +# ifdef __STDC__ + void GC_debug_free(GC_PTR p) +# else + void GC_debug_free(p) + GC_PTR p; +# endif +{ + register GC_PTR base = GC_base(p); + register ptr_t clobbered; + + if (base == 0) { + GC_err_printf1("Attempt to free invalid pointer %lx\n", + (unsigned long)p); + if (p != 0) ABORT("free(invalid pointer)"); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { + GC_err_printf1( + "GC_debug_free called on pointer %lx wo debugging info\n", + (unsigned long)p); + } else { + clobbered = GC_check_annotated_obj((oh *)base); + if (clobbered != 0) { + if (((oh *)base) -> oh_sz == GC_size(base)) { + GC_err_printf0( + "GC_debug_free: found previously deallocated (?) object at "); + } else { + GC_err_printf0("GC_debug_free: found smashed location at "); + } + GC_print_smashed_obj(p, clobbered); + } + /* Invalidate size */ + ((oh *)base) -> oh_sz = GC_size(base); + } + if (GC_find_leak) { + GC_free(base); + } else { + register hdr * hhdr = HDR(p); + GC_bool uncollectable = FALSE; + + if (hhdr -> hb_obj_kind == UNCOLLECTABLE) { + uncollectable = TRUE; + } +# ifdef ATOMIC_UNCOLLECTABLE + if (hhdr -> hb_obj_kind == AUNCOLLECTABLE) { + uncollectable = TRUE; + } +# endif + if (uncollectable) GC_free(base); + } /* !GC_find_leak */ +} + +# ifdef __STDC__ + GC_PTR GC_debug_realloc(GC_PTR p, size_t lb, EXTRA_ARGS) +# else + GC_PTR GC_debug_realloc(p, lb, s, i) + GC_PTR p; + size_t lb; + char *s; + int i; +# endif +{ + register GC_PTR base = GC_base(p); + register ptr_t clobbered; + register GC_PTR result; + register size_t copy_sz = lb; + register size_t old_sz; + register hdr * hhdr; + + if (p == 0) return(GC_debug_malloc(lb, OPT_RA s, i)); + if (base == 0) { + GC_err_printf1( + "Attempt to reallocate invalid pointer %lx\n", (unsigned long)p); + ABORT("realloc(invalid pointer)"); + } + if ((ptr_t)p - (ptr_t)base != sizeof(oh)) { + GC_err_printf1( + "GC_debug_realloc called on pointer %lx wo debugging info\n", + (unsigned long)p); + return(GC_realloc(p, lb)); + } + hhdr = HDR(base); + switch (hhdr -> hb_obj_kind) { +# ifdef STUBBORN_ALLOC + case STUBBORN: + result = GC_debug_malloc_stubborn(lb, OPT_RA s, i); + break; +# endif + case NORMAL: + result = GC_debug_malloc(lb, OPT_RA s, i); + break; + case PTRFREE: + result = GC_debug_malloc_atomic(lb, OPT_RA s, i); + break; + case UNCOLLECTABLE: + result = GC_debug_malloc_uncollectable(lb, OPT_RA s, i); + break; +# ifdef ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + result = GC_debug_malloc_atomic_uncollectable(lb, OPT_RA s, i); + break; +# endif + default: + GC_err_printf0("GC_debug_realloc: encountered bad kind\n"); + ABORT("bad kind"); + } + clobbered = GC_check_annotated_obj((oh *)base); + if (clobbered != 0) { + GC_err_printf0("GC_debug_realloc: found smashed location at "); + GC_print_smashed_obj(p, clobbered); + } + old_sz = ((oh *)base) -> oh_sz; + if (old_sz < copy_sz) copy_sz = old_sz; + if (result == 0) return(0); + BCOPY(p, result, copy_sz); + GC_debug_free(p); + return(result); +} + +/* Check all marked objects in the given block for validity */ +/*ARGSUSED*/ +void GC_check_heap_block(hbp, dummy) +register struct hblk *hbp; /* ptr to current heap block */ +word dummy; +{ + register struct hblkhdr * hhdr = HDR(hbp); + register word sz = hhdr -> hb_sz; + register int word_no; + register word *p, *plim; + + p = (word *)(hbp->hb_body); + word_no = HDR_WORDS; + if (sz > MAXOBJSZ) { + plim = p; + } else { + plim = (word *)((((word)hbp) + HBLKSIZE) - WORDS_TO_BYTES(sz)); + } + /* go through all words in block */ + while( p <= plim ) { + if( mark_bit_from_hdr(hhdr, word_no) + && GC_has_debug_info((ptr_t)p)) { + ptr_t clobbered = GC_check_annotated_obj((oh *)p); + + if (clobbered != 0) { + GC_err_printf0( + "GC_check_heap_block: found smashed location at "); + GC_print_smashed_obj((ptr_t)p, clobbered); + } + } + word_no += sz; + p += sz; + } +} + + +/* This assumes that all accessible objects are marked, and that */ +/* I hold the allocation lock. Normally called by collector. */ +void GC_check_heap_proc() +{ +# ifndef SMALL_CONFIG + if (sizeof(oh) & (2 * sizeof(word) - 1) != 0) { + ABORT("Alignment problem: object header has inappropriate size\n"); + } +# endif + GC_apply_to_all_blocks(GC_check_heap_block, (word)0); +} + +struct closure { + GC_finalization_proc cl_fn; + GC_PTR cl_data; +}; + +# ifdef __STDC__ + void * GC_make_closure(GC_finalization_proc fn, void * data) +# else + GC_PTR GC_make_closure(fn, data) + GC_finalization_proc fn; + GC_PTR data; +# endif +{ + struct closure * result = + (struct closure *) GC_malloc(sizeof (struct closure)); + + result -> cl_fn = fn; + result -> cl_data = data; + return((GC_PTR)result); +} + +# ifdef __STDC__ + void GC_debug_invoke_finalizer(void * obj, void * data) +# else + void GC_debug_invoke_finalizer(obj, data) + char * obj; + char * data; +# endif +{ + register struct closure * cl = (struct closure *) data; + + (*(cl -> cl_fn))((GC_PTR)((char *)obj + sizeof(oh)), cl -> cl_data); +} + + +# ifdef __STDC__ + void GC_debug_register_finalizer(GC_PTR obj, GC_finalization_proc fn, + GC_PTR cd, GC_finalization_proc *ofn, + GC_PTR *ocd) +# else + void GC_debug_register_finalizer(obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc *ofn; + GC_PTR *ocd; +# endif +{ + ptr_t base = GC_base(obj); + if (0 == base || (ptr_t)obj - base != sizeof(oh)) { + GC_err_printf1( + "GC_register_finalizer called with non-base-pointer 0x%lx\n", + obj); + } + GC_register_finalizer(base, GC_debug_invoke_finalizer, + GC_make_closure(fn,cd), ofn, ocd); +} + +# ifdef __STDC__ + void GC_debug_register_finalizer_no_order + (GC_PTR obj, GC_finalization_proc fn, + GC_PTR cd, GC_finalization_proc *ofn, + GC_PTR *ocd) +# else + void GC_debug_register_finalizer_no_order + (obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc *ofn; + GC_PTR *ocd; +# endif +{ + ptr_t base = GC_base(obj); + if (0 == base || (ptr_t)obj - base != sizeof(oh)) { + GC_err_printf1( + "GC_register_finalizer_no_order called with non-base-pointer 0x%lx\n", + obj); + } + GC_register_finalizer_no_order(base, GC_debug_invoke_finalizer, + GC_make_closure(fn,cd), ofn, ocd); + } + +# ifdef __STDC__ + void GC_debug_register_finalizer_ignore_self + (GC_PTR obj, GC_finalization_proc fn, + GC_PTR cd, GC_finalization_proc *ofn, + GC_PTR *ocd) +# else + void GC_debug_register_finalizer_ignore_self + (obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc *ofn; + GC_PTR *ocd; +# endif +{ + ptr_t base = GC_base(obj); + if (0 == base || (ptr_t)obj - base != sizeof(oh)) { + GC_err_printf1( + "GC_register_finalizer_ignore_self called with non-base-pointer 0x%lx\n", + obj); + } + GC_register_finalizer_ignore_self(base, GC_debug_invoke_finalizer, + GC_make_closure(fn,cd), ofn, ocd); +} diff --git a/gc/dyn_load.c b/gc/dyn_load.c new file mode 100644 index 0000000..d3df0a0 --- /dev/null +++ b/gc/dyn_load.c @@ -0,0 +1,799 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1997 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Original author: Bill Janssen + * Heavily modified by Hans Boehm and others + */ + +/* + * This is incredibly OS specific code for tracking down data sections in + * dynamic libraries. There appears to be no way of doing this quickly + * without groveling through undocumented data structures. We would argue + * that this is a bug in the design of the dlopen interface. THIS CODE + * MAY BREAK IN FUTURE OS RELEASES. If this matters to you, don't hesitate + * to let your vendor know ... + * + * None of this is safe with dlclose and incremental collection. + * But then not much of anything is safe in the presence of dlclose. + */ +#ifndef MACOS +# include <sys/types.h> +#endif +#include "gc_priv.h" + +/* BTL: avoid circular redefinition of dlopen if SOLARIS_THREADS defined */ +# if defined(SOLARIS_THREADS) && defined(dlopen) + /* To support threads in Solaris, gc.h interposes on dlopen by */ + /* defining "dlopen" to be "GC_dlopen", which is implemented below. */ + /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */ + /* real system dlopen() in their implementation. We first remove */ + /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */ +# undef dlopen +# define GC_must_restore_redefined_dlopen +# else +# undef GC_must_restore_redefined_dlopen +# endif + +#if (defined(DYNAMIC_LOADING) || defined(MSWIN32)) && !defined(PCR) +#if !defined(SUNOS4) && !defined(SUNOS5DL) && !defined(IRIX5) && \ + !defined(MSWIN32) && !(defined(ALPHA) && defined(OSF1)) && \ + !defined(HP_PA) && !(defined(LINUX) && defined(__ELF__)) && \ + !defined(RS6000) && !defined(SCO_ELF) + --> We only know how to find data segments of dynamic libraries for the + --> above. Additional SVR4 variants might not be too + --> hard to add. +#endif + +#include <stdio.h> +#ifdef SUNOS5DL +# include <sys/elf.h> +# include <dlfcn.h> +# include <link.h> +#endif +#ifdef SUNOS4 +# include <dlfcn.h> +# include <link.h> +# include <a.out.h> + /* struct link_map field overrides */ +# define l_next lm_next +# define l_addr lm_addr +# define l_name lm_name +#endif + + +#if defined(SUNOS5DL) && !defined(USE_PROC_FOR_LIBRARIES) + +#ifdef LINT + Elf32_Dyn _DYNAMIC; +#endif + +static struct link_map * +GC_FirstDLOpenedLinkMap() +{ + extern Elf32_Dyn _DYNAMIC; + Elf32_Dyn *dp; + struct r_debug *r; + static struct link_map * cachedResult = 0; + static Elf32_Dyn *dynStructureAddr = 0; + /* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */ + +# ifdef SUNOS53_SHARED_LIB + /* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set */ + /* up properly in dynamically linked .so's. This means we have */ + /* to use its value in the set of original object files loaded */ + /* at program startup. */ + if( dynStructureAddr == 0 ) { + void* startupSyms = dlopen(0, RTLD_LAZY); + dynStructureAddr = (Elf32_Dyn*)dlsym(startupSyms, "_DYNAMIC"); + } +# else + dynStructureAddr = &_DYNAMIC; +# endif + + if( dynStructureAddr == 0) { + return(0); + } + if( cachedResult == 0 ) { + int tag; + for( dp = ((Elf32_Dyn *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) { + if( tag == DT_DEBUG ) { + struct link_map *lm + = ((struct r_debug *)(dp->d_un.d_ptr))->r_map; + if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */ + break; + } + } + } + return cachedResult; +} + +#endif /* SUNOS5DL ... */ + +#if defined(SUNOS4) && !defined(USE_PROC_FOR_LIBRARIES) + +#ifdef LINT + struct link_dynamic _DYNAMIC; +#endif + +static struct link_map * +GC_FirstDLOpenedLinkMap() +{ + extern struct link_dynamic _DYNAMIC; + + if( &_DYNAMIC == 0) { + return(0); + } + return(_DYNAMIC.ld_un.ld_1->ld_loaded); +} + +/* Return the address of the ld.so allocated common symbol */ +/* with the least address, or 0 if none. */ +static ptr_t GC_first_common() +{ + ptr_t result = 0; + extern struct link_dynamic _DYNAMIC; + struct rtc_symb * curr_symbol; + + if( &_DYNAMIC == 0) { + return(0); + } + curr_symbol = _DYNAMIC.ldd -> ldd_cp; + for (; curr_symbol != 0; curr_symbol = curr_symbol -> rtc_next) { + if (result == 0 + || (ptr_t)(curr_symbol -> rtc_sp -> n_value) < result) { + result = (ptr_t)(curr_symbol -> rtc_sp -> n_value); + } + } + return(result); +} + +#endif /* SUNOS4 ... */ + +# if defined(SUNOS4) || defined(SUNOS5DL) +/* Add dynamic library data sections to the root set. */ +# if !defined(PCR) && !defined(SOLARIS_THREADS) && defined(THREADS) +# ifndef SRC_M3 + --> fix mutual exclusion with dlopen +# endif /* We assume M3 programs don't call dlopen for now */ +# endif + +# ifdef SOLARIS_THREADS + /* Redefine dlopen to guarantee mutual exclusion with */ + /* GC_register_dynamic_libraries. */ + /* assumes that dlopen doesn't need to call GC_malloc */ + /* and friends. */ +# include <thread.h> +# include <synch.h> + +void * GC_dlopen(const char *path, int mode) +{ + void * result; + +# ifndef USE_PROC_FOR_LIBRARIES + mutex_lock(&GC_allocate_ml); +# endif + result = dlopen(path, mode); +# ifndef USE_PROC_FOR_LIBRARIES + mutex_unlock(&GC_allocate_ml); +# endif + return(result); +} +# endif /* SOLARIS_THREADS */ + +/* BTL: added to fix circular dlopen definition if SOLARIS_THREADS defined */ +# if defined(GC_must_restore_redefined_dlopen) +# define dlopen GC_dlopen +# endif + +# ifndef USE_PROC_FOR_LIBRARIES +void GC_register_dynamic_libraries() +{ + struct link_map *lm = GC_FirstDLOpenedLinkMap(); + + + for (lm = GC_FirstDLOpenedLinkMap(); + lm != (struct link_map *) 0; lm = lm->l_next) + { +# ifdef SUNOS4 + struct exec *e; + + e = (struct exec *) lm->lm_addr; + GC_add_roots_inner( + ((char *) (N_DATOFF(*e) + lm->lm_addr)), + ((char *) (N_BSSADDR(*e) + e->a_bss + lm->lm_addr)), + TRUE); +# endif +# ifdef SUNOS5DL + Elf32_Ehdr * e; + Elf32_Phdr * p; + unsigned long offset; + char * start; + register int i; + + e = (Elf32_Ehdr *) lm->l_addr; + p = ((Elf32_Phdr *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner( + start, + start + p->p_memsz, + TRUE + ); + } + break; + default: + break; + } + } +# endif + } +# ifdef SUNOS4 + { + static ptr_t common_start = 0; + ptr_t common_end; + extern ptr_t GC_find_limit(); + + if (common_start == 0) common_start = GC_first_common(); + if (common_start != 0) { + common_end = GC_find_limit(common_start, TRUE); + GC_add_roots_inner((char *)common_start, (char *)common_end, TRUE); + } + } +# endif +} + +# endif /* !USE_PROC ... */ +# endif /* SUNOS */ + +#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) + +/* Dynamic loading code for Linux running ELF. Somewhat tested on + * Linux/x86, untested but hopefully should work on Linux/Alpha. + * This code was derived from the Solaris/ELF support. Thanks to + * whatever kind soul wrote that. - Patrick Bridges */ + +#include <elf.h> +#include <link.h> + +/* Newer versions of Linux/Alpha and Linux/x86 define this macro. We + * define it for those older versions that don't. */ +# ifndef ElfW +# if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32 +# define ElfW(type) Elf32_##type +# else +# define ElfW(type) Elf64_##type +# endif +# endif + +static struct link_map * +GC_FirstDLOpenedLinkMap() +{ +# ifdef __GNUC__ +# pragma weak _DYNAMIC +# endif + extern ElfW(Dyn) _DYNAMIC[]; + ElfW(Dyn) *dp; + struct r_debug *r; + static struct link_map *cachedResult = 0; + + if( _DYNAMIC == 0) { + return(0); + } + if( cachedResult == 0 ) { + int tag; + for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) { + if( tag == DT_DEBUG ) { + struct link_map *lm + = ((struct r_debug *)(dp->d_un.d_ptr))->r_map; + if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */ + break; + } + } + } + return cachedResult; +} + + +void GC_register_dynamic_libraries() +{ + struct link_map *lm = GC_FirstDLOpenedLinkMap(); + + + for (lm = GC_FirstDLOpenedLinkMap(); + lm != (struct link_map *) 0; lm = lm->l_next) + { + ElfW(Ehdr) * e; + ElfW(Phdr) * p; + unsigned long offset; + char * start; + register int i; + + e = (ElfW(Ehdr) *) lm->l_addr; + p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff)); + offset = ((unsigned long)(lm->l_addr)); + for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) { + switch( p->p_type ) { + case PT_LOAD: + { + if( !(p->p_flags & PF_W) ) break; + start = ((char *)(p->p_vaddr)) + offset; + GC_add_roots_inner(start, start + p->p_memsz, TRUE); + } + break; + default: + break; + } + } + } +} + +#endif + +#if defined(IRIX5) || defined(USE_PROC_FOR_LIBRARIES) + +#include <sys/procfs.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <elf.h> +#include <errno.h> + +extern void * GC_roots_present(); + /* The type is a lie, since the real type doesn't make sense here, */ + /* and we only test for NULL. */ + +extern ptr_t GC_scratch_last_end_ptr; /* End of GC_scratch_alloc arena */ + +/* We use /proc to track down all parts of the address space that are */ +/* mapped by the process, and throw out regions we know we shouldn't */ +/* worry about. This may also work under other SVR4 variants. */ +void GC_register_dynamic_libraries() +{ + static int fd = -1; + char buf[30]; + static prmap_t * addr_map = 0; + static int current_sz = 0; /* Number of records currently in addr_map */ + static int needed_sz; /* Required size of addr_map */ + register int i; + register long flags; + register ptr_t start; + register ptr_t limit; + ptr_t heap_start = (ptr_t)HEAP_START; + ptr_t heap_end = heap_start; + +# ifdef SUNOS5DL +# define MA_PHYS 0 +# endif /* SUNOS5DL */ + + if (fd < 0) { + sprintf(buf, "/proc/%d", getpid()); + /* The above generates a lint complaint, since pid_t varies. */ + /* It's unclear how to improve this. */ + fd = open(buf, O_RDONLY); + if (fd < 0) { + ABORT("/proc open failed"); + } + } + if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) { + GC_err_printf2("fd = %d, errno = %d\n", fd, errno); + ABORT("/proc PIOCNMAP ioctl failed"); + } + if (needed_sz >= current_sz) { + current_sz = needed_sz * 2 + 1; + /* Expansion, plus room for 0 record */ + addr_map = (prmap_t *)GC_scratch_alloc((word) + (current_sz * sizeof(prmap_t))); + } + if (ioctl(fd, PIOCMAP, addr_map) < 0) { + GC_err_printf4("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n", + fd, errno, needed_sz, addr_map); + ABORT("/proc PIOCMAP ioctl failed"); + }; + if (GC_n_heap_sects > 0) { + heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start + + GC_heap_sects[GC_n_heap_sects-1].hs_bytes; + if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr; + } + for (i = 0; i < needed_sz; i++) { + flags = addr_map[i].pr_mflags; + if ((flags & (MA_BREAK | MA_STACK | MA_PHYS)) != 0) goto irrelevant; + if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE)) + goto irrelevant; + /* The latter test is empirically useless. Other than the */ + /* main data and stack segments, everything appears to be */ + /* mapped readable, writable, executable, and shared(!!). */ + /* This makes no sense to me. - HB */ + start = (ptr_t)(addr_map[i].pr_vaddr); + if (GC_roots_present(start)) goto irrelevant; + if (start < heap_end && start >= heap_start) + goto irrelevant; +# ifdef MMAP_STACKS + if (GC_is_thread_stack(start)) goto irrelevant; +# endif /* MMAP_STACKS */ + + limit = start + addr_map[i].pr_size; + if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) { + /* Discard text segments, i.e. 0-offset mappings against */ + /* executable files which appear to have ELF headers. */ + caddr_t arg; + int obj; +# define MAP_IRR_SZ 10 + static ptr_t map_irr[MAP_IRR_SZ]; + /* Known irrelevant map entries */ + static int n_irr = 0; + struct stat buf; + register int i; + + for (i = 0; i < n_irr; i++) { + if (map_irr[i] == start) goto irrelevant; + } + arg = (caddr_t)start; + obj = ioctl(fd, PIOCOPENM, &arg); + if (obj >= 0) { + fstat(obj, &buf); + close(obj); + if ((buf.st_mode & 0111) != 0) { + if (n_irr < MAP_IRR_SZ) { + map_irr[n_irr++] = start; + } + goto irrelevant; + } + } + } + GC_add_roots_inner(start, limit, TRUE); + irrelevant: ; + } + /* Dont keep cached descriptor, for now. Some kernels don't like us */ + /* to keep a /proc file descriptor around during kill -9. */ + if (close(fd) < 0) ABORT("Couldnt close /proc file"); + fd = -1; +} + +# endif /* USE_PROC || IRIX5 */ + +# ifdef MSWIN32 + +# define WIN32_LEAN_AND_MEAN +# define NOSERVICE +# include <windows.h> +# include <stdlib.h> + + /* We traverse the entire address space and register all segments */ + /* that could possibly have been written to. */ + DWORD GC_allocation_granularity; + + extern GC_bool GC_is_heap_base (ptr_t p); + +# ifdef WIN32_THREADS + extern void GC_get_next_stack(char *start, char **lo, char **hi); +# endif + + void GC_cond_add_roots(char *base, char * limit) + { + char dummy; + char * stack_top + = (char *) ((word)(&dummy) & ~(GC_allocation_granularity-1)); + if (base == limit) return; +# ifdef WIN32_THREADS + { + char * curr_base = base; + char * next_stack_lo; + char * next_stack_hi; + + for(;;) { + GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi); + if (next_stack_lo >= limit) break; + GC_add_roots_inner(curr_base, next_stack_lo, TRUE); + curr_base = next_stack_hi; + } + if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE); + } +# else + if (limit > stack_top && base < GC_stackbottom) { + /* Part of the stack; ignore it. */ + return; + } + GC_add_roots_inner(base, limit, TRUE); +# endif + } + + extern GC_bool GC_win32s; + + void GC_register_dynamic_libraries() + { + MEMORY_BASIC_INFORMATION buf; + SYSTEM_INFO sysinfo; + DWORD result; + DWORD protect; + LPVOID p; + char * base; + char * limit, * new_limit; + + if (GC_win32s) return; + GetSystemInfo(&sysinfo); + base = limit = p = sysinfo.lpMinimumApplicationAddress; + GC_allocation_granularity = sysinfo.dwAllocationGranularity; + while (p < sysinfo.lpMaximumApplicationAddress) { + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && (protect == PAGE_EXECUTE_READWRITE + || protect == PAGE_READWRITE) + && !GC_is_heap_base(buf.AllocationBase)) { + if ((char *)p == limit) { + limit = new_limit; + } else { + GC_cond_add_roots(base, limit); + base = p; + limit = new_limit; + } + } + if (p > (LPVOID)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + GC_cond_add_roots(base, limit); + } + +#endif /* MSWIN32 */ + +#if defined(ALPHA) && defined(OSF1) + +#include <loader.h> + +void GC_register_dynamic_libraries() +{ + int status; + ldr_process_t mypid; + + /* module */ + ldr_module_t moduleid = LDR_NULL_MODULE; + ldr_module_info_t moduleinfo; + size_t moduleinfosize = sizeof(moduleinfo); + size_t modulereturnsize; + + /* region */ + ldr_region_t region; + ldr_region_info_t regioninfo; + size_t regioninfosize = sizeof(regioninfo); + size_t regionreturnsize; + + /* Obtain id of this process */ + mypid = ldr_my_process(); + + /* For each module */ + while (TRUE) { + + /* Get the next (first) module */ + status = ldr_next_module(mypid, &moduleid); + + /* Any more modules? */ + if (moduleid == LDR_NULL_MODULE) + break; /* No more modules */ + + /* Check status AFTER checking moduleid because */ + /* of a bug in the non-shared ldr_next_module stub */ + if (status != 0 ) { + GC_printf1("dynamic_load: status = %ld\n", (long)status); + { + extern char *sys_errlist[]; + extern int sys_nerr; + extern int errno; + if (errno <= sys_nerr) { + GC_printf1("dynamic_load: %s\n", (long)sys_errlist[errno]); + } else { + GC_printf1("dynamic_load: %d\n", (long)errno); + } + } + ABORT("ldr_next_module failed"); + } + + /* Get the module information */ + status = ldr_inq_module(mypid, moduleid, &moduleinfo, + moduleinfosize, &modulereturnsize); + if (status != 0 ) + ABORT("ldr_inq_module failed"); + + /* is module for the main program (i.e. nonshared portion)? */ + if (moduleinfo.lmi_flags & LDR_MAIN) + continue; /* skip the main module */ + +# ifdef VERBOSE + GC_printf("---Module---\n"); + GC_printf("Module ID = %16ld\n", moduleinfo.lmi_modid); + GC_printf("Count of regions = %16d\n", moduleinfo.lmi_nregion); + GC_printf("flags for module = %16lx\n", moduleinfo.lmi_flags); + GC_printf("pathname of module = \"%s\"\n", moduleinfo.lmi_name); +# endif + + /* For each region in this module */ + for (region = 0; region < moduleinfo.lmi_nregion; region++) { + + /* Get the region information */ + status = ldr_inq_region(mypid, moduleid, region, ®ioninfo, + regioninfosize, ®ionreturnsize); + if (status != 0 ) + ABORT("ldr_inq_region failed"); + + /* only process writable (data) regions */ + if (! (regioninfo.lri_prot & LDR_W)) + continue; + +# ifdef VERBOSE + GC_printf("--- Region ---\n"); + GC_printf("Region number = %16ld\n", + regioninfo.lri_region_no); + GC_printf("Protection flags = %016x\n", regioninfo.lri_prot); + GC_printf("Virtual address = %16p\n", regioninfo.lri_vaddr); + GC_printf("Mapped address = %16p\n", regioninfo.lri_mapaddr); + GC_printf("Region size = %16ld\n", regioninfo.lri_size); + GC_printf("Region name = \"%s\"\n", regioninfo.lri_name); +# endif + + /* register region as a garbage collection root */ + GC_add_roots_inner ( + (char *)regioninfo.lri_mapaddr, + (char *)regioninfo.lri_mapaddr + regioninfo.lri_size, + TRUE); + + } + } +} +#endif + +#if defined(HP_PA) + +#include <errno.h> +#include <dl.h> + +extern int errno; +extern char *sys_errlist[]; +extern int sys_nerr; + +void GC_register_dynamic_libraries() +{ + int status; + int index = 1; /* Ordinal position in shared library search list */ + struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */ + + /* For each dynamic library loaded */ + while (TRUE) { + + /* Get info about next shared library */ + status = shl_get(index, &shl_desc); + + /* Check if this is the end of the list or if some error occured */ + if (status != 0) { + if (errno == EINVAL) { + break; /* Moved past end of shared library list --> finished */ + } else { + if (errno <= sys_nerr) { + GC_printf1("dynamic_load: %s\n", (long) sys_errlist[errno]); + } else { + GC_printf1("dynamic_load: %d\n", (long) errno); + } + ABORT("shl_get failed"); + } + } + +# ifdef VERBOSE + GC_printf0("---Shared library---\n"); + GC_printf1("\tfilename = \"%s\"\n", shl_desc->filename); + GC_printf1("\tindex = %d\n", index); + GC_printf1("\thandle = %08x\n", + (unsigned long) shl_desc->handle); + GC_printf1("\ttext seg. start = %08x\n", shl_desc->tstart); + GC_printf1("\ttext seg. end = %08x\n", shl_desc->tend); + GC_printf1("\tdata seg. start = %08x\n", shl_desc->dstart); + GC_printf1("\tdata seg. end = %08x\n", shl_desc->dend); + GC_printf1("\tref. count = %lu\n", shl_desc->ref_count); +# endif + + /* register shared library's data segment as a garbage collection root */ + GC_add_roots_inner((char *) shl_desc->dstart, + (char *) shl_desc->dend, TRUE); + + index++; + } +} +#endif /* HP_PA */ + +#ifdef RS6000 +#pragma alloca +#include <sys/ldr.h> +#include <sys/errno.h> +void GC_register_dynamic_libraries() +{ + int len; + char *ldibuf; + int ldibuflen; + struct ld_info *ldi; + + ldibuf = alloca(ldibuflen = 8192); + + while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) { + if (errno != ENOMEM) { + ABORT("loadquery failed"); + } + ldibuf = alloca(ldibuflen *= 2); + } + + ldi = (struct ld_info *)ldibuf; + while (ldi) { + len = ldi->ldinfo_next; + GC_add_roots_inner( + ldi->ldinfo_dataorg, + (unsigned long)ldi->ldinfo_dataorg + + ldi->ldinfo_datasize, + TRUE); + ldi = len ? (struct ld_info *)((char *)ldi + len) : 0; + } +} +#endif /* RS6000 */ + + + +#else /* !DYNAMIC_LOADING */ + +#ifdef PCR + +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" + +void GC_register_dynamic_libraries() +{ + /* Add new static data areas of dynamically loaded modules. */ + { + PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile(); + PCR_IL_LoadedSegment * q; + + /* Skip uncommited files */ + while (p != NIL && !(p -> lf_commitPoint)) { + /* The loading of this file has not yet been committed */ + /* Hence its description could be inconsistent. */ + /* Furthermore, it hasn't yet been run. Hence its data */ + /* segments can't possibly reference heap allocated */ + /* objects. */ + p = p -> lf_prev; + } + for (; p != NIL; p = p -> lf_prev) { + for (q = p -> lf_ls; q != NIL; q = q -> ls_next) { + if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK) + == PCR_IL_SegFlags_Traced_on) { + GC_add_roots_inner + ((char *)(q -> ls_addr), + (char *)(q -> ls_addr) + q -> ls_bytes, + TRUE); + } + } + } + } +} + + +#else /* !PCR */ + +void GC_register_dynamic_libraries(){} + +int GC_no_dynamic_loading; + +#endif /* !PCR */ +#endif /* !DYNAMIC_LOADING */ diff --git a/gc/finalize.c b/gc/finalize.c new file mode 100644 index 0000000..2ee927f --- /dev/null +++ b/gc/finalize.c @@ -0,0 +1,758 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved. + + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 1, 1996 1:19 pm PST */ +# define I_HIDE_POINTERS +# include "gc_priv.h" +# include "gc_mark.h" + +# ifdef FINALIZE_ON_DEMAND + int GC_finalize_on_demand = 1; +# else + int GC_finalize_on_demand = 0; +# endif + +# ifdef JAVA_FINALIZATION + int GC_java_finalization = 1; +# else + int GC_java_finalization = 0; +# endif + +/* Type of mark procedure used for marking from finalizable object. */ +/* This procedure normally does not mark the object, only its */ +/* descendents. */ +typedef void finalization_mark_proc(/* ptr_t finalizable_obj_ptr */); + +# define HASH3(addr,size,log_size) \ + ((((word)(addr) >> 3) ^ ((word)(addr) >> (3+(log_size)))) \ + & ((size) - 1)) +#define HASH2(addr,log_size) HASH3(addr, 1 << log_size, log_size) + +struct hash_chain_entry { + word hidden_key; + struct hash_chain_entry * next; +}; + +unsigned GC_finalization_failures = 0; + /* Number of finalization requests that failed for lack of memory. */ + +static struct disappearing_link { + struct hash_chain_entry prolog; +# define dl_hidden_link prolog.hidden_key + /* Field to be cleared. */ +# define dl_next(x) (struct disappearing_link *)((x) -> prolog.next) +# define dl_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) + + word dl_hidden_obj; /* Pointer to object base */ +} **dl_head = 0; + +static signed_word log_dl_table_size = -1; + /* Binary log of */ + /* current size of array pointed to by dl_head. */ + /* -1 ==> size is 0. */ + +word GC_dl_entries = 0; /* Number of entries currently in disappearing */ + /* link table. */ + +static struct finalizable_object { + struct hash_chain_entry prolog; +# define fo_hidden_base prolog.hidden_key + /* Pointer to object base. */ + /* No longer hidden once object */ + /* is on finalize_now queue. */ +# define fo_next(x) (struct finalizable_object *)((x) -> prolog.next) +# define fo_set_next(x,y) (x) -> prolog.next = (struct hash_chain_entry *)(y) + GC_finalization_proc fo_fn; /* Finalizer. */ + ptr_t fo_client_data; + word fo_object_size; /* In bytes. */ + finalization_mark_proc * fo_mark_proc; /* Mark-through procedure */ +} **fo_head = 0; + +struct finalizable_object * GC_finalize_now = 0; + /* LIst of objects that should be finalized now. */ + +static signed_word log_fo_table_size = -1; + +word GC_fo_entries = 0; + +# ifdef SRC_M3 +void GC_push_finalizer_structures() +{ + GC_push_all((ptr_t)(&dl_head), (ptr_t)(&dl_head) + sizeof(word)); + GC_push_all((ptr_t)(&fo_head), (ptr_t)(&fo_head) + sizeof(word)); +} +# endif + +/* Double the size of a hash table. *size_ptr is the log of its current */ +/* size. May be a noop. */ +/* *table is a pointer to an array of hash headers. If we succeed, we */ +/* update both *table and *log_size_ptr. */ +/* Lock is held. Signals are disabled. */ +void GC_grow_table(table, log_size_ptr) +struct hash_chain_entry ***table; +signed_word * log_size_ptr; +{ + register word i; + register struct hash_chain_entry *p; + int log_old_size = *log_size_ptr; + register int log_new_size = log_old_size + 1; + word old_size = ((log_old_size == -1)? 0: (1 << log_old_size)); + register word new_size = 1 << log_new_size; + struct hash_chain_entry **new_table = (struct hash_chain_entry **) + GC_generic_malloc_inner_ignore_off_page( + (size_t)new_size * sizeof(struct hash_chain_entry *), NORMAL); + + if (new_table == 0) { + if (table == 0) { + ABORT("Insufficient space for initial table allocation"); + } else { + return; + } + } + for (i = 0; i < old_size; i++) { + p = (*table)[i]; + while (p != 0) { + register ptr_t real_key = (ptr_t)REVEAL_POINTER(p -> hidden_key); + register struct hash_chain_entry *next = p -> next; + register int new_hash = HASH3(real_key, new_size, log_new_size); + + p -> next = new_table[new_hash]; + new_table[new_hash] = p; + p = next; + } + } + *log_size_ptr = log_new_size; + *table = new_table; +} + +# if defined(__STDC__) || defined(__cplusplus) + int GC_register_disappearing_link(GC_PTR * link) +# else + int GC_register_disappearing_link(link) + GC_PTR * link; +# endif +{ + ptr_t base; + + base = (ptr_t)GC_base((GC_PTR)link); + if (base == 0) + ABORT("Bad arg to GC_register_disappearing_link"); + return(GC_general_register_disappearing_link(link, base)); +} + +# if defined(__STDC__) || defined(__cplusplus) + int GC_general_register_disappearing_link(GC_PTR * link, + GC_PTR obj) +# else + int GC_general_register_disappearing_link(link, obj) + GC_PTR * link; + GC_PTR obj; +# endif + +{ + struct disappearing_link *curr_dl; + int index; + struct disappearing_link * new_dl; + DCL_LOCK_STATE; + + if ((word)link & (ALIGNMENT-1)) + ABORT("Bad arg to GC_general_register_disappearing_link"); +# ifdef THREADS + DISABLE_SIGNALS(); + LOCK(); +# endif + if (log_dl_table_size == -1 + || GC_dl_entries > ((word)1 << log_dl_table_size)) { +# ifndef THREADS + DISABLE_SIGNALS(); +# endif + GC_grow_table((struct hash_chain_entry ***)(&dl_head), + &log_dl_table_size); +# ifdef PRINTSTATS + GC_printf1("Grew dl table to %lu entries\n", + (unsigned long)(1 << log_dl_table_size)); +# endif +# ifndef THREADS + ENABLE_SIGNALS(); +# endif + } + index = HASH2(link, log_dl_table_size); + curr_dl = dl_head[index]; + for (curr_dl = dl_head[index]; curr_dl != 0; curr_dl = dl_next(curr_dl)) { + if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { + curr_dl -> dl_hidden_obj = HIDE_POINTER(obj); +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return(1); + } + } +# ifdef THREADS + new_dl = (struct disappearing_link *) + GC_generic_malloc_inner(sizeof(struct disappearing_link),NORMAL); +# else + new_dl = (struct disappearing_link *) + GC_malloc(sizeof(struct disappearing_link)); +# endif + if (new_dl != 0) { + new_dl -> dl_hidden_obj = HIDE_POINTER(obj); + new_dl -> dl_hidden_link = HIDE_POINTER(link); + dl_set_next(new_dl, dl_head[index]); + dl_head[index] = new_dl; + GC_dl_entries++; + } else { + GC_finalization_failures++; + } +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return(0); +} + +# if defined(__STDC__) || defined(__cplusplus) + int GC_unregister_disappearing_link(GC_PTR * link) +# else + int GC_unregister_disappearing_link(link) + GC_PTR * link; +# endif +{ + struct disappearing_link *curr_dl, *prev_dl; + int index; + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + index = HASH2(link, log_dl_table_size); + if (((unsigned long)link & (ALIGNMENT-1))) goto out; + prev_dl = 0; curr_dl = dl_head[index]; + while (curr_dl != 0) { + if (curr_dl -> dl_hidden_link == HIDE_POINTER(link)) { + if (prev_dl == 0) { + dl_head[index] = dl_next(curr_dl); + } else { + dl_set_next(prev_dl, dl_next(curr_dl)); + } + GC_dl_entries--; + UNLOCK(); + ENABLE_SIGNALS(); + GC_free((GC_PTR)curr_dl); + return(1); + } + prev_dl = curr_dl; + curr_dl = dl_next(curr_dl); + } +out: + UNLOCK(); + ENABLE_SIGNALS(); + return(0); +} + +/* Possible finalization_marker procedures. Note that mark stack */ +/* overflow is handled by the caller, and is not a disaster. */ +GC_API void GC_normal_finalize_mark_proc(p) +ptr_t p; +{ + hdr * hhdr = HDR(p); + + PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top, + &(GC_mark_stack[GC_mark_stack_size])); +} + +/* This only pays very partial attention to the mark descriptor. */ +/* It does the right thing for normal and atomic objects, and treats */ +/* most others as normal. */ +GC_API void GC_ignore_self_finalize_mark_proc(p) +ptr_t p; +{ + hdr * hhdr = HDR(p); + word descr = hhdr -> hb_descr; + ptr_t q, r; + ptr_t scan_limit; + ptr_t target_limit = p + WORDS_TO_BYTES(hhdr -> hb_sz) - 1; + + if ((descr & DS_TAGS) == DS_LENGTH) { + scan_limit = p + descr - sizeof(word); + } else { + scan_limit = target_limit + 1 - sizeof(word); + } + for (q = p; q <= scan_limit; q += ALIGNMENT) { + r = *(ptr_t *)q; + if (r < p || r > target_limit) { + GC_PUSH_ONE_HEAP((word)r, q); + } + } +} + +/*ARGSUSED*/ +GC_API void GC_null_finalize_mark_proc(p) +ptr_t p; +{ +} + + + +/* Register a finalization function. See gc.h for details. */ +/* in the nonthreads case, we try to avoid disabling signals, */ +/* since it can be expensive. Threads packages typically */ +/* make it cheaper. */ +/* The last parameter is a procedure that determines */ +/* marking for finalization ordering. Any objects marked */ +/* by that procedure will be guaranteed to not have been */ +/* finalized when this finalizer is invoked. */ +GC_API void GC_register_finalizer_inner(obj, fn, cd, ofn, ocd, mp) +GC_PTR obj; +GC_finalization_proc fn; +GC_PTR cd; +GC_finalization_proc * ofn; +GC_PTR * ocd; +finalization_mark_proc * mp; +{ + ptr_t base; + struct finalizable_object * curr_fo, * prev_fo; + int index; + struct finalizable_object *new_fo; + DCL_LOCK_STATE; + +# ifdef THREADS + DISABLE_SIGNALS(); + LOCK(); +# endif + if (log_fo_table_size == -1 + || GC_fo_entries > ((word)1 << log_fo_table_size)) { +# ifndef THREADS + DISABLE_SIGNALS(); +# endif + GC_grow_table((struct hash_chain_entry ***)(&fo_head), + &log_fo_table_size); +# ifdef PRINTSTATS + GC_printf1("Grew fo table to %lu entries\n", + (unsigned long)(1 << log_fo_table_size)); +# endif +# ifndef THREADS + ENABLE_SIGNALS(); +# endif + } + /* in the THREADS case signals are disabled and we hold allocation */ + /* lock; otherwise neither is true. Proceed carefully. */ + base = (ptr_t)obj; + index = HASH2(base, log_fo_table_size); + prev_fo = 0; curr_fo = fo_head[index]; + while (curr_fo != 0) { + if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) { + /* Interruption by a signal in the middle of this */ + /* should be safe. The client may see only *ocd */ + /* updated, but we'll declare that to be his */ + /* problem. */ + if (ocd) *ocd = (GC_PTR) curr_fo -> fo_client_data; + if (ofn) *ofn = curr_fo -> fo_fn; + /* Delete the structure for base. */ + if (prev_fo == 0) { + fo_head[index] = fo_next(curr_fo); + } else { + fo_set_next(prev_fo, fo_next(curr_fo)); + } + if (fn == 0) { + GC_fo_entries--; + /* May not happen if we get a signal. But a high */ + /* estimate will only make the table larger than */ + /* necessary. */ +# ifndef THREADS + GC_free((GC_PTR)curr_fo); +# endif + } else { + curr_fo -> fo_fn = fn; + curr_fo -> fo_client_data = (ptr_t)cd; + curr_fo -> fo_mark_proc = mp; + /* Reinsert it. We deleted it first to maintain */ + /* consistency in the event of a signal. */ + if (prev_fo == 0) { + fo_head[index] = curr_fo; + } else { + fo_set_next(prev_fo, curr_fo); + } + } +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return; + } + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + if (ofn) *ofn = 0; + if (ocd) *ocd = 0; + if (fn == 0) { +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return; + } +# ifdef THREADS + new_fo = (struct finalizable_object *) + GC_generic_malloc_inner(sizeof(struct finalizable_object),NORMAL); +# else + new_fo = (struct finalizable_object *) + GC_malloc(sizeof(struct finalizable_object)); +# endif + if (new_fo != 0) { + new_fo -> fo_hidden_base = (word)HIDE_POINTER(base); + new_fo -> fo_fn = fn; + new_fo -> fo_client_data = (ptr_t)cd; + new_fo -> fo_object_size = GC_size(base); + new_fo -> fo_mark_proc = mp; + fo_set_next(new_fo, fo_head[index]); + GC_fo_entries++; + fo_head[index] = new_fo; + } else { + GC_finalization_failures++; + } +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif +} + +# if defined(__STDC__) + void GC_register_finalizer(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +# else + void GC_register_finalizer(obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc * ofn; + GC_PTR * ocd; +# endif +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_normal_finalize_mark_proc); +} + +# if defined(__STDC__) + void GC_register_finalizer_ignore_self(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +# else + void GC_register_finalizer_ignore_self(obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc * ofn; + GC_PTR * ocd; +# endif +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_ignore_self_finalize_mark_proc); +} + +# if defined(__STDC__) + void GC_register_finalizer_no_order(void * obj, + GC_finalization_proc fn, void * cd, + GC_finalization_proc *ofn, void ** ocd) +# else + void GC_register_finalizer_no_order(obj, fn, cd, ofn, ocd) + GC_PTR obj; + GC_finalization_proc fn; + GC_PTR cd; + GC_finalization_proc * ofn; + GC_PTR * ocd; +# endif +{ + GC_register_finalizer_inner(obj, fn, cd, ofn, + ocd, GC_null_finalize_mark_proc); +} + +/* Called with world stopped. Cause disappearing links to disappear, */ +/* and invoke finalizers. */ +void GC_finalize() +{ + struct disappearing_link * curr_dl, * prev_dl, * next_dl; + struct finalizable_object * curr_fo, * prev_fo, * next_fo; + ptr_t real_ptr, real_link; + register int i; + int dl_size = (log_dl_table_size == -1 ) ? 0 : (1 << log_dl_table_size); + int fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); + + /* Make disappearing links disappear */ + for (i = 0; i < dl_size; i++) { + curr_dl = dl_head[i]; + prev_dl = 0; + while (curr_dl != 0) { + real_ptr = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_obj); + real_link = (ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link); + if (!GC_is_marked(real_ptr)) { + *(word *)real_link = 0; + next_dl = dl_next(curr_dl); + if (prev_dl == 0) { + dl_head[i] = next_dl; + } else { + dl_set_next(prev_dl, next_dl); + } + GC_clear_mark_bit((ptr_t)curr_dl); + GC_dl_entries--; + curr_dl = next_dl; + } else { + prev_dl = curr_dl; + curr_dl = dl_next(curr_dl); + } + } + } + /* Mark all objects reachable via chains of 1 or more pointers */ + /* from finalizable objects. */ +# ifdef PRINTSTATS + if (GC_mark_state != MS_NONE) ABORT("Bad mark state"); +# endif + for (i = 0; i < fo_size; i++) { + for (curr_fo = fo_head[i]; curr_fo != 0; curr_fo = fo_next(curr_fo)) { + real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + GC_MARKED_FOR_FINALIZATION(real_ptr); + GC_MARK_FO(real_ptr, curr_fo -> fo_mark_proc); + if (GC_is_marked(real_ptr)) { + WARN("Finalization cycle involving %lx\n", real_ptr); + } + } + } + } + /* Enqueue for finalization all objects that are still */ + /* unreachable. */ + GC_words_finalized = 0; + for (i = 0; i < fo_size; i++) { + curr_fo = fo_head[i]; + prev_fo = 0; + while (curr_fo != 0) { + real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); + if (!GC_is_marked(real_ptr)) { + if (!GC_java_finalization) { + GC_set_mark_bit(real_ptr); + } + /* Delete from hash table */ + next_fo = fo_next(curr_fo); + if (prev_fo == 0) { + fo_head[i] = next_fo; + } else { + fo_set_next(prev_fo, next_fo); + } + GC_fo_entries--; + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_finalize_now); + GC_finalize_now = curr_fo; + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_words_finalized += + ALIGNED_WORDS(curr_fo -> fo_object_size) + + ALIGNED_WORDS(sizeof(struct finalizable_object)); +# ifdef PRINTSTATS + if (!GC_is_marked((ptr_t)curr_fo)) { + ABORT("GC_finalize: found accessible unmarked object\n"); + } +# endif + curr_fo = next_fo; + } else { + prev_fo = curr_fo; + curr_fo = fo_next(curr_fo); + } + } + } + + if (GC_java_finalization) { + /* make sure we mark everything reachable from objects finalized + using the no_order mark_proc */ + for (curr_fo = GC_finalize_now; + curr_fo != NULL; curr_fo = fo_next(curr_fo)) { + real_ptr = (ptr_t)curr_fo -> fo_hidden_base; + if (!GC_is_marked(real_ptr)) { + if (curr_fo -> fo_mark_proc == GC_null_finalize_mark_proc) { + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + } + GC_set_mark_bit(real_ptr); + } + } + } + + /* Remove dangling disappearing links. */ + for (i = 0; i < dl_size; i++) { + curr_dl = dl_head[i]; + prev_dl = 0; + while (curr_dl != 0) { + real_link = GC_base((ptr_t)REVEAL_POINTER(curr_dl -> dl_hidden_link)); + if (real_link != 0 && !GC_is_marked(real_link)) { + next_dl = dl_next(curr_dl); + if (prev_dl == 0) { + dl_head[i] = next_dl; + } else { + dl_set_next(prev_dl, next_dl); + } + GC_clear_mark_bit((ptr_t)curr_dl); + GC_dl_entries--; + curr_dl = next_dl; + } else { + prev_dl = curr_dl; + curr_dl = dl_next(curr_dl); + } + } + } +} + +#ifndef JAVA_FINALIZATION_NOT_NEEDED + +/* Enqueue all remaining finalizers to be run - Assumes lock is + * held, and signals are disabled */ +void GC_enqueue_all_finalizers() +{ + struct finalizable_object * curr_fo, * prev_fo, * next_fo; + ptr_t real_ptr, real_link; + register int i; + int fo_size; + + fo_size = (log_fo_table_size == -1 ) ? 0 : (1 << log_fo_table_size); + GC_words_finalized = 0; + for (i = 0; i < fo_size; i++) { + curr_fo = fo_head[i]; + prev_fo = 0; + while (curr_fo != 0) { + real_ptr = (ptr_t)REVEAL_POINTER(curr_fo -> fo_hidden_base); + GC_MARK_FO(real_ptr, GC_normal_finalize_mark_proc); + GC_set_mark_bit(real_ptr); + + /* Delete from hash table */ + next_fo = fo_next(curr_fo); + if (prev_fo == 0) { + fo_head[i] = next_fo; + } else { + fo_set_next(prev_fo, next_fo); + } + GC_fo_entries--; + + /* Add to list of objects awaiting finalization. */ + fo_set_next(curr_fo, GC_finalize_now); + GC_finalize_now = curr_fo; + + /* unhide object pointer so any future collections will */ + /* see it. */ + curr_fo -> fo_hidden_base = + (word) REVEAL_POINTER(curr_fo -> fo_hidden_base); + + GC_words_finalized += + ALIGNED_WORDS(curr_fo -> fo_object_size) + + ALIGNED_WORDS(sizeof(struct finalizable_object)); + curr_fo = next_fo; + } + } + + return; +} + +/* Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * Unfortunately, the Java standard implies we have to keep running + * finalizers until there are no more left, a potential infinite loop. + * YUCK. + * Note that this is even more dangerous than the usual Java + * finalizers, in that objects reachable from static variables + * may have been finalized when these finalizers are run. + * Finalizers run at this point must be prepared to deal with a + * mostly broken world. + * This routine is externally callable, so is called without + * the allocation lock. + */ +GC_API void GC_finalize_all() +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + while (GC_fo_entries > 0) { + GC_enqueue_all_finalizers(); + UNLOCK(); + ENABLE_SIGNALS(); + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + } + UNLOCK(); + ENABLE_SIGNALS(); +} +#endif + +/* Invoke finalizers for all objects that are ready to be finalized. */ +/* Should be called without allocation lock. */ +int GC_invoke_finalizers() +{ + register struct finalizable_object * curr_fo; + register int count = 0; + DCL_LOCK_STATE; + + while (GC_finalize_now != 0) { +# ifdef THREADS + DISABLE_SIGNALS(); + LOCK(); +# endif + curr_fo = GC_finalize_now; +# ifdef THREADS + if (curr_fo != 0) GC_finalize_now = fo_next(curr_fo); + UNLOCK(); + ENABLE_SIGNALS(); + if (curr_fo == 0) break; +# else + GC_finalize_now = fo_next(curr_fo); +# endif + fo_set_next(curr_fo, 0); + (*(curr_fo -> fo_fn))((ptr_t)(curr_fo -> fo_hidden_base), + curr_fo -> fo_client_data); + curr_fo -> fo_client_data = 0; + ++count; +# ifdef UNDEFINED + /* This is probably a bad idea. It throws off accounting if */ + /* nearly all objects are finalizable. O.w. it shouldn't */ + /* matter. */ + GC_free((GC_PTR)curr_fo); +# endif + } + return count; +} + +# ifdef __STDC__ + GC_PTR GC_call_with_alloc_lock(GC_fn_type fn, + GC_PTR client_data) +# else + GC_PTR GC_call_with_alloc_lock(fn, client_data) + GC_fn_type fn; + GC_PTR client_data; +# endif +{ + GC_PTR result; + DCL_LOCK_STATE; + +# ifdef THREADS + DISABLE_SIGNALS(); + LOCK(); + SET_LOCK_HOLDER(); +# endif + result = (*fn)(client_data); +# ifdef THREADS + UNSET_LOCK_HOLDER(); + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return(result); +} @@ -0,0 +1,754 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * If you are using Solaris threads, look at the end of this file. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef _GC_H + +# define _GC_H +# define __GC +# include <stddef.h> + +#if defined(__CYGWIN32__) && defined(GC_USE_DLL) +#include "libgc_globals.h" +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif +#endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif +#endif + +#ifndef GC_API +#define GC_API extern +#endif + +# if defined(__STDC__) || defined(__cplusplus) +# define GC_PROTO(args) args + typedef void * GC_PTR; +# else +# define GC_PROTO(args) () + typedef char * GC_PTR; +# endif + +# ifdef __cplusplus + extern "C" { +# endif + + +/* Define word and signed_word to be unsigned and signed types of the */ +/* size as char * or void *. There seems to be no way to do this */ +/* even semi-portably. The following is probably no better/worse */ +/* than almost anything else. */ +/* The ANSI standard suggests that size_t and ptr_diff_t might be */ +/* better choices. But those appear to have incorrect definitions */ +/* on may systems. Notably "typedef int size_t" seems to be both */ +/* frequent and WRONG. */ +typedef unsigned long GC_word; +typedef long GC_signed_word; + +/* Public read-only variables */ + +GC_API GC_word GC_gc_no;/* Counter incremented per collection. */ + /* Includes empty GCs at startup. */ + + +/* Public R/W variables */ + +GC_API GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)); + /* When there is insufficient memory to satisfy */ + /* an allocation request, we return */ + /* (*GC_oom_fn)(). By default this just */ + /* returns 0. */ + /* If it returns, it must return 0 or a valid */ + /* pointer to a previously allocated heap */ + /* object. */ + +GC_API int GC_find_leak; + /* Do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_free. Initial value */ + /* is determined by FIND_LEAK macro. */ + +GC_API int GC_quiet; /* Disable statistics output. Only matters if */ + /* collector has been compiled with statistics */ + /* enabled. This involves a performance cost, */ + /* and is thus not the default. */ + +GC_API int GC_finalize_on_demand; + /* If nonzero, finalizers will only be run in */ + /* response to an eplit GC_invoke_finalizers */ + /* call. The default is determined by whether */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. */ + +GC_API int GC_java_finalization; + /* Mark objects reachable from finalizable */ + /* objects in a separate postpass. This makes */ + /* it a bit safer to use non-topologically- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + +GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */ + /* because it's not safe. */ + +GC_API int GC_dont_expand; + /* Dont expand heap unless explicitly requested */ + /* or forced to. */ + +GC_API int GC_full_freq; /* Number of partial collections between */ + /* full collections. Matters only if */ + /* GC_incremental is set. */ + +GC_API GC_word GC_non_gc_bytes; + /* Bytes not considered candidates for collection. */ + /* Used only to control scheduling of collections. */ + +GC_API GC_word GC_free_space_divisor; + /* We try to make sure that we allocate at */ + /* least N/GC_free_space_divisor bytes between */ + /* collections, where N is the heap size plus */ + /* a rough estimate of the root set size. */ + /* Initially, GC_free_space_divisor = 4. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* GC_free_space_divisor = 1 will effectively */ + /* disable collections. */ + +GC_API GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. Initially 0. */ + + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + +/* Public procedures */ +/* + * general purpose allocation routines, with roughly malloc calling conv. + * The atomic versions promise that no relevant pointers are contained + * in the object. The nonatomic versions guarantee that the new object + * is cleared. GC_malloc_stubborn promises that no changes to the object + * will occur after GC_end_stubborn_change has been called on the + * result of GC_malloc_stubborn. GC_malloc_uncollectable allocates an object + * that is scanned for pointers to collectable objects, but is not itself + * collectable. GC_malloc_uncollectable and GC_free called on the resulting + * object implicitly update GC_non_gc_bytes appropriately. + */ +GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes)); + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes)); + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* If the argument is stubborn, it should not be changeable when freed. */ +/* An object should not be enable for finalization when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_free GC_PROTO((GC_PTR object_addr)); + +/* + * Stubborn objects may be changed only if the collector is explicitly informed. + * The collector is implicitly informed of coming change when such + * an object is first allocated. The following routines inform the + * collector that an object will no longer be changed, or that it will + * once again be changed. Only nonNIL pointer stores into the object + * are considered to be changes. The argument to GC_end_stubborn_change + * must be exacly the value returned by GC_malloc_stubborn or passed to + * GC_change_stubborn. (In the second case it may be an interior pointer + * within 512 bytes of the beginning of the objects.) + * There is a performance penalty for allowing more than + * one stubborn object to be changed at once, but it is acceptable to + * do so. The same applies to dropping stubborn objects that are still + * changeable. + */ +GC_API void GC_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR)); + +/* Return a pointer to the base (lowest address) of an object given */ +/* a pointer to a location within the object. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer)); + +/* Given a pointer to the base of an object, return its size in bytes. */ +/* The returned size may be slightly larger than what was originally */ +/* requested. */ +GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); + +/* For compatibility with C library. This is occasionally faster than */ +/* a malloc followed by a bcopy. But if you rely on that, either here */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* If the argument is stubborn, the result will have changes enabled. */ +/* It is an error to have changes enabled for the original object. */ +/* Follows ANSI comventions for NULL old_object. */ +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); + +/* Explicitly increase the heap size. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes)); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. */ +GC_API void GC_set_max_heap_size GC_PROTO((GC_word n)); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* need not be scanned. This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be */ +/* mistaken for dynamic library data segments on some systems. */ +GC_API void GC_exclude_static_roots GC_PROTO((GC_PTR start, GC_PTR finish)); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_clear_roots GC_PROTO((void)); + +/* Add a root segment. Wizards only. */ +GC_API void GC_add_roots GC_PROTO((char * low_address, + char * high_address_plus_1)); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to n. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector was compiled with recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_register_displacement GC_PROTO((GC_word n)); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_debug_register_displacement GC_PROTO((GC_word n)); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_gcollect GC_PROTO((void)); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. */ +/* Return 0 if the collection was aborted, 1 if it succeeded. */ +typedef int (* GC_stop_func) GC_PROTO((void)); +GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func)); + +/* Return the number of bytes in the heap. Excludes collector private */ +/* data structures. Includes empty blocks and fragmentation loss. */ +/* Includes some pages that were allocated but never written. */ +GC_API size_t GC_get_heap_size GC_PROTO((void)); + +/* Return the number of bytes allocated since the last collection. */ +GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); + +/* Enable incremental/generational collection. */ +/* Not advisable unless dirty bits are */ +/* available or most heap objects are */ +/* pointerfree(atomic) or immutable. */ +/* Don't use in leak finding mode. */ +/* Ignored if GC_dont_gc is true. */ +GC_API void GC_enable_incremental GC_PROTO((void)); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done. */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. */ +GC_API int GC_collect_a_little GC_PROTO((void)); + +/* Allocate an object of size lb bytes. The client guarantees that */ +/* as long as the object is live, it will be referenced by a pointer */ +/* that points to somewhere within the first 256 bytes of the object. */ +/* (This should normally be declared volatile to prevent the compiler */ +/* from invalidating this assertion.) This routine is only useful */ +/* if a large array is being allocated. It reduces the chance of */ +/* accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or MS Windows system this is recommended */ +/* for arrays likely to be larger than 100K or so. For other systems, */ +/* or if the collector is not configured to recognize all interior */ +/* pointers, the threshold is normally much higher. */ +GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); +GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#ifdef GC_ADD_CALLER +# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +# define GC_EXTRA_PARAMS GC_word ra, char * descr_string, int descr_int +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + +/* Debugging (annotated) allocation. GC_gcollect will check */ +/* objects allocated in this way for overwrites, etc. */ +GC_API GC_PTR GC_debug_malloc + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_atomic + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_uncollectable + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_stubborn + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); +GC_API GC_PTR GC_debug_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, + GC_EXTRA_PARAMS)); + +GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); +# ifdef GC_DEBUG +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS); +# define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, GC_base(obj)) +# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +# else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz) +# define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +# endif +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecifed size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. */ +# define GC_NEW(t) (t *)GC_MALLOC(sizeof (t)) +# define GC_NEW_ATOMIC(t) (t *)GC_MALLOC_ATOMIC(sizeof (t)) +# define GC_NEW_STUBBORN(t) (t *)GC_MALLOC_STUBBORN(sizeof (t)) +# define GC_NEW_UNCOLLECTABLE(t) (t *)GC_MALLOC_UNCOLLECTABLE(sizeof (t)) + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (*GC_finalization_proc) + GC_PROTO((GC_PTR obj, GC_PTR client_data)); + +GC_API void GC_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + /* When obj is no longer accessible, invoke */ + /* (*fn)(obj, cd). If a and b are inaccessible, and */ + /* a points to b (after disappearing links have been */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered just before */ + /* they are invoked. */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocation lock is held, and */ + /* such conversions are not performed by finalization */ + /* routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the nonNULL starting address of an */ + /* object allocated by GC_malloc or friends. */ + /* Note that any garbage collectable object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. */ +GC_API void GC_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* where p is a pointer that is not followed by finalization */ +/* code, and should not be considered in determining */ +/* finalization order. */ +GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Link should point to a field of a heap allocated */ + /* object obj. *link will be cleared when obj is */ + /* found to be inaccessible. This happens BEFORE any */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. */ + /* Returns 1 if link was already registered, 0 */ + /* otherwise. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_general_register_disappearing_link + GC_PROTO((GC_PTR * /* link */, GC_PTR obj)); + /* A slight generalization of the above. *link is */ + /* cleared when obj first becomes inaccessible. This */ + /* can be used to implement weak pointers easily and */ + /* safely. Typically link will point to a location */ + /* holding a disguised pointer to obj. (A pointer */ + /* inside an "atomic" object is effectively */ + /* disguised.) In this way soft */ + /* pointers are broken before any object */ + /* reachable from them are finalized. Each link */ + /* May be registered only once, i.e. with one obj */ + /* value. This was added after a long email discussion */ + /* with John Ellis. */ + /* Obj must be a pointer to the first word of an object */ + /* we allocated. It is unsafe to explicitly deallocate */ + /* the object containing link. Explicitly deallocating */ + /* obj may or may not cause link to eventually be */ + /* cleared. */ +GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Returns 0 if link was not actually registered. */ + /* Undoes a registration by either of the above two */ + /* routines. */ + +/* Auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); +GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); + +GC_API int GC_invoke_finalizers GC_PROTO((void)); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC-finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* p may not be a NULL pointer. */ +typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); +GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); + /* Returns old warning procedure. */ + +/* The following is intended to be used by a higher level */ +/* (e.g. cedar-like) finalization facility. It is expected */ +/* that finalization code will arrange for hidden pointers to */ +/* disappear. Otherwise objects can be accessed after they */ +/* have been collected. */ +/* Note that putting pointers in atomic objects or in */ +/* nonpointer slots of "typed" objects is equivalent to */ +/* disguising them in this way, and may have other advantages. */ +# if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) + typedef GC_word GC_hidden_pointer; +# define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +# define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) + /* Converting a hidden pointer to a real pointer requires verifying */ + /* that the object still exists. This involves acquiring the */ + /* allocator lock to avoid a race with the collector. */ +# endif /* I_HIDE_POINTERS */ + +typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); +GC_API GC_PTR GC_call_with_alloc_lock + GC_PROTO((GC_fn_type fn, GC_PTR client_data)); + +/* Check that p and q point to the same object. */ +/* Fail conspicuously if they don't. */ +/* Returns the first argument. */ +/* Succeeds if neither p nor q points to the heap. */ +/* May succeed if both p and q point to between heap objects. */ +GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q)); + +/* Checked pointer pre- and post- increment operations. Note that */ +/* the second argument is in units of bytes, not multiples of the */ +/* object size. This should either be invoked from a macro, or the */ +/* call should be automatically generated. */ +GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much)); +GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much)); + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't fail conspicuously. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for multithreaded worlds. */ +GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p)); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Fail conspicuously if this property does not hold. */ +/* Uninteresting with ALL_INTERIOR_POINTERS. */ +/* Always returns its argument. */ +GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p)); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +#ifdef GC_DEBUG +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr(&(x), (n)*sizeof(*x)) +# define GC_POST_INCR2(x, type_of_result) \ + ((type_of_result)GC_post_incr(&(x), sizeof(*x)) +# ifdef __GNUC__ +# define GC_PTR_ADD(x, n) \ + GC_PTR_ADD3(x, n, typeof(x)) +# define GC_PRE_INCR(x, n) \ + GC_PRE_INCR3(x, n, typeof(x)) +# define GC_POST_INCR(x, n) \ + GC_POST_INCR3(x, typeof(x)) +# else + /* We can't do this right without typeof, which ANSI */ + /* decided was not sufficiently useful. Repeatedly */ + /* mentioning the arguments seems too dangerous to be */ + /* useful. So does not casting the result. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_ADD3(x, n, type_of_result) ((x)+(n)) +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR3(x, n, type_of_result) ((x) += (n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR2(x, n, type_of_result) ((x)++) +# define GC_POST_INCR(x, n) ((x)++) +#endif + +/* Safer assignment of a pointer to a nonstack location. */ +#ifdef GC_DEBUG +# ifdef __STDC__ +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# else +# define GC_PTR_STORE(p, q) \ + (*(char **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_STORE(p, q) *((p) = (q)) +#endif + +/* Fynctions called to report pointer checking errors */ +GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q)); + +GC_API void (*GC_is_valid_displacement_print_proc) + GC_PROTO((GC_PTR p)); + +GC_API void (*GC_is_visible_print_proc) + GC_PROTO((GC_PTR p)); + +#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +#endif + +#ifdef SOLARIS_THREADS +/* We need to intercept calls to many of the threads primitives, so */ +/* that we can locate thread stacks and stop the world. */ +/* Note also that the collector cannot see thread specific data. */ +/* Thread specific data should generally consist of pointers to */ +/* uncollectable objects, which are deallocated using the destructor */ +/* facility in thr_keycreate. */ +# include <thread.h> +# include <signal.h> + int GC_thr_create(void *stack_base, size_t stack_size, + void *(*start_routine)(void *), void *arg, long flags, + thread_t *new_thread); + int GC_thr_join(thread_t wait_for, thread_t *departed, void **status); + int GC_thr_suspend(thread_t target_thread); + int GC_thr_continue(thread_t target_thread); + void * GC_dlopen(const char *path, int mode); + +# ifdef _SOLARIS_PTHREADS +# include <pthread.h> + extern int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void * (*thread_execp)(void *), void *arg); + extern int GC_pthread_join(pthread_t wait_for, void **status); + +# undef thread_t + +# define pthread_join GC_pthread_join +# define pthread_create GC_pthread_create +#endif + +# define thr_create GC_thr_create +# define thr_join GC_thr_join +# define thr_suspend GC_thr_suspend +# define thr_continue GC_thr_continue +# define dlopen GC_dlopen + +# endif /* SOLARIS_THREADS */ + + +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) +/* We treat these similarly. */ +# include <pthread.h> +# include <signal.h> + + int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); + int GC_pthread_join(pthread_t thread, void **retval); + +# define pthread_create GC_pthread_create +# define pthread_sigmask GC_pthread_sigmask +# define pthread_join GC_pthread_join + +#endif /* IRIX_THREADS || LINUX_THREADS */ + +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) + /* Any flavor of threads except SRC_M3. */ +/* This returns a list of objects, linked through their first */ +/* word. Its use can greatly reduce lock contention problems, since */ +/* the allocation lock can be acquired and released many fewer times. */ +/* lb must be large enough to hold the pointer field. */ +GC_PTR GC_malloc_many(size_t lb); +#define GC_NEXT(p) (*(GC_PTR *)(p)) /* Retrieve the next element */ + /* in returned list. */ +extern void GC_thr_init(); /* Needed for Solaris/X86 */ + +#endif /* THREADS && !SRC_M3 */ + +/* + * If you are planning on putting + * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() + * from the statically loaded program section. + * This circumvents a Solaris 2.X (X<=4) linker bug. + */ +#if defined(sparc) || defined(__sparc) +# define GC_INIT() { extern end, etext; \ + GC_noop(&end, &etext); } +#else +# if defined(__CYGWIN32__) && defined(GC_USE_DLL) + /* + * Similarly gnu-win32 DLLs need explicit initialization + */ +# define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } +# else +# define GC_INIT() +# endif +#endif + +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); +#endif + +#ifdef __cplusplus + } /* end of extern "C" */ +#endif + +#endif /* _GC_H */ diff --git a/gc/gc.mak b/gc/gc.mak new file mode 100644 index 0000000..0fd22b7 --- /dev/null +++ b/gc/gc.mak @@ -0,0 +1,2087 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.10 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +!IF "$(CFG)" == "" +CFG=gctest - Win32 Release +!MESSAGE No configuration specified. Defaulting to cord - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "gc - Win32 Release" && "$(CFG)" != "gc - Win32 Debug" &&\ + "$(CFG)" != "gctest - Win32 Release" && "$(CFG)" != "gctest - Win32 Debug" &&\ + "$(CFG)" != "cord - Win32 Release" && "$(CFG)" != "cord - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "gc.mak" CFG="cord - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "gc - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gc - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "gctest - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "gctest - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "cord - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "cord - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "gctest - Win32 Debug" + +!IF "$(CFG)" == "gc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : ".\Release\gc.dll" ".\Release\gc.bsc" + +CLEAN : + -@erase ".\Release\allchblk.obj" + -@erase ".\Release\allchblk.sbr" + -@erase ".\Release\alloc.obj" + -@erase ".\Release\alloc.sbr" + -@erase ".\Release\blacklst.obj" + -@erase ".\Release\blacklst.sbr" + -@erase ".\Release\checksums.obj" + -@erase ".\Release\checksums.sbr" + -@erase ".\Release\dbg_mlc.obj" + -@erase ".\Release\dbg_mlc.sbr" + -@erase ".\Release\dyn_load.obj" + -@erase ".\Release\dyn_load.sbr" + -@erase ".\Release\finalize.obj" + -@erase ".\Release\finalize.sbr" + -@erase ".\Release\gc.bsc" + -@erase ".\Release\gc.dll" + -@erase ".\Release\gc.exp" + -@erase ".\Release\gc.lib" + -@erase ".\Release\headers.obj" + -@erase ".\Release\headers.sbr" + -@erase ".\Release\mach_dep.obj" + -@erase ".\Release\mach_dep.sbr" + -@erase ".\Release\malloc.obj" + -@erase ".\Release\malloc.sbr" + -@erase ".\Release\mallocx.obj" + -@erase ".\Release\mallocx.sbr" + -@erase ".\Release\mark.obj" + -@erase ".\Release\mark.sbr" + -@erase ".\Release\mark_rts.obj" + -@erase ".\Release\mark_rts.sbr" + -@erase ".\Release\misc.obj" + -@erase ".\Release\misc.sbr" + -@erase ".\Release\new_hblk.obj" + -@erase ".\Release\new_hblk.sbr" + -@erase ".\Release\obj_map.obj" + -@erase ".\Release\obj_map.sbr" + -@erase ".\Release\os_dep.obj" + -@erase ".\Release\os_dep.sbr" + -@erase ".\Release\ptr_chck.obj" + -@erase ".\Release\ptr_chck.sbr" + -@erase ".\Release\reclaim.obj" + -@erase ".\Release\reclaim.sbr" + -@erase ".\Release\stubborn.obj" + -@erase ".\Release\stubborn.sbr" + -@erase ".\Release\typd_mlc.obj" + -@erase ".\Release\typd_mlc.sbr" + -@erase ".\Release\win32_threads.obj" + -@erase ".\Release\win32_threads.sbr" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "SILENT" /D "GC_BUILD" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "SILENT" /D "GC_BUILD" /D\ + "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D\ + "WIN32_THREADS" /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\Release/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc" +BSC32_SBRS= \ + ".\Release\allchblk.sbr" \ + ".\Release\alloc.sbr" \ + ".\Release\blacklst.sbr" \ + ".\Release\checksums.sbr" \ + ".\Release\dbg_mlc.sbr" \ + ".\Release\dyn_load.sbr" \ + ".\Release\finalize.sbr" \ + ".\Release\headers.sbr" \ + ".\Release\mach_dep.sbr" \ + ".\Release\malloc.sbr" \ + ".\Release\mallocx.sbr" \ + ".\Release\mark.sbr" \ + ".\Release\mark_rts.sbr" \ + ".\Release\misc.sbr" \ + ".\Release\new_hblk.sbr" \ + ".\Release\obj_map.sbr" \ + ".\Release\os_dep.sbr" \ + ".\Release\ptr_chck.sbr" \ + ".\Release\reclaim.sbr" \ + ".\Release\stubborn.sbr" \ + ".\Release\typd_mlc.sbr" \ + ".\Release\win32_threads.sbr" + +".\Release\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/gc.pdb" /machine:I386 /out:"$(OUTDIR)/gc.dll"\ + /implib:"$(OUTDIR)/gc.lib" +LINK32_OBJS= \ + ".\Release\allchblk.obj" \ + ".\Release\alloc.obj" \ + ".\Release\blacklst.obj" \ + ".\Release\checksums.obj" \ + ".\Release\dbg_mlc.obj" \ + ".\Release\dyn_load.obj" \ + ".\Release\finalize.obj" \ + ".\Release\headers.obj" \ + ".\Release\mach_dep.obj" \ + ".\Release\malloc.obj" \ + ".\Release\mallocx.obj" \ + ".\Release\mark.obj" \ + ".\Release\mark_rts.obj" \ + ".\Release\misc.obj" \ + ".\Release\new_hblk.obj" \ + ".\Release\obj_map.obj" \ + ".\Release\os_dep.obj" \ + ".\Release\ptr_chck.obj" \ + ".\Release\reclaim.obj" \ + ".\Release\stubborn.obj" \ + ".\Release\typd_mlc.obj" \ + ".\Release\win32_threads.obj" + +".\Release\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : ".\Debug\gc.dll" ".\Debug\gc.bsc" + +CLEAN : + -@erase ".\Debug\allchblk.obj" + -@erase ".\Debug\allchblk.sbr" + -@erase ".\Debug\alloc.obj" + -@erase ".\Debug\alloc.sbr" + -@erase ".\Debug\blacklst.obj" + -@erase ".\Debug\blacklst.sbr" + -@erase ".\Debug\checksums.obj" + -@erase ".\Debug\checksums.sbr" + -@erase ".\Debug\dbg_mlc.obj" + -@erase ".\Debug\dbg_mlc.sbr" + -@erase ".\Debug\dyn_load.obj" + -@erase ".\Debug\dyn_load.sbr" + -@erase ".\Debug\finalize.obj" + -@erase ".\Debug\finalize.sbr" + -@erase ".\Debug\gc.bsc" + -@erase ".\Debug\gc.dll" + -@erase ".\Debug\gc.exp" + -@erase ".\Debug\gc.lib" + -@erase ".\Debug\gc.map" + -@erase ".\Debug\gc.pdb" + -@erase ".\Debug\headers.obj" + -@erase ".\Debug\headers.sbr" + -@erase ".\Debug\mach_dep.obj" + -@erase ".\Debug\mach_dep.sbr" + -@erase ".\Debug\malloc.obj" + -@erase ".\Debug\malloc.sbr" + -@erase ".\Debug\mallocx.obj" + -@erase ".\Debug\mallocx.sbr" + -@erase ".\Debug\mark.obj" + -@erase ".\Debug\mark.sbr" + -@erase ".\Debug\mark_rts.obj" + -@erase ".\Debug\mark_rts.sbr" + -@erase ".\Debug\misc.obj" + -@erase ".\Debug\misc.sbr" + -@erase ".\Debug\new_hblk.obj" + -@erase ".\Debug\new_hblk.sbr" + -@erase ".\Debug\obj_map.obj" + -@erase ".\Debug\obj_map.sbr" + -@erase ".\Debug\os_dep.obj" + -@erase ".\Debug\os_dep.sbr" + -@erase ".\Debug\ptr_chck.obj" + -@erase ".\Debug\ptr_chck.sbr" + -@erase ".\Debug\reclaim.obj" + -@erase ".\Debug\reclaim.sbr" + -@erase ".\Debug\stubborn.obj" + -@erase ".\Debug\stubborn.sbr" + -@erase ".\Debug\typd_mlc.obj" + -@erase ".\Debug\typd_mlc.sbr" + -@erase ".\Debug\vc40.idb" + -@erase ".\Debug\vc40.pdb" + -@erase ".\Debug\win32_threads.obj" + -@erase ".\Debug\win32_threads.sbr" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "SILENT" /D "GC_BUILD" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "SILENT" /D "GC_BUILD"\ + /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D\ + "WIN32_THREADS" /FR"$(INTDIR)/" /Fp"$(INTDIR)/gc.pch" /YX /Fo"$(INTDIR)/"\ + /Fd"$(INTDIR)/" /c +CPP_OBJS=.\Debug/ +CPP_SBRS=.\Debug/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gc.bsc" +BSC32_SBRS= \ + ".\Debug\allchblk.sbr" \ + ".\Debug\alloc.sbr" \ + ".\Debug\blacklst.sbr" \ + ".\Debug\checksums.sbr" \ + ".\Debug\dbg_mlc.sbr" \ + ".\Debug\dyn_load.sbr" \ + ".\Debug\finalize.sbr" \ + ".\Debug\headers.sbr" \ + ".\Debug\mach_dep.sbr" \ + ".\Debug\malloc.sbr" \ + ".\Debug\mallocx.sbr" \ + ".\Debug\mark.sbr" \ + ".\Debug\mark_rts.sbr" \ + ".\Debug\misc.sbr" \ + ".\Debug\new_hblk.sbr" \ + ".\Debug\obj_map.sbr" \ + ".\Debug\os_dep.sbr" \ + ".\Debug\ptr_chck.sbr" \ + ".\Debug\reclaim.sbr" \ + ".\Debug\stubborn.sbr" \ + ".\Debug\typd_mlc.sbr" \ + ".\Debug\win32_threads.sbr" + +".\Debug\gc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /dll /incremental:no\ + /pdb:"$(OUTDIR)/gc.pdb" /map:"$(INTDIR)/gc.map" /debug /machine:I386\ + /out:"$(OUTDIR)/gc.dll" /implib:"$(OUTDIR)/gc.lib" +LINK32_OBJS= \ + ".\Debug\allchblk.obj" \ + ".\Debug\alloc.obj" \ + ".\Debug\blacklst.obj" \ + ".\Debug\checksums.obj" \ + ".\Debug\dbg_mlc.obj" \ + ".\Debug\dyn_load.obj" \ + ".\Debug\finalize.obj" \ + ".\Debug\headers.obj" \ + ".\Debug\mach_dep.obj" \ + ".\Debug\malloc.obj" \ + ".\Debug\mallocx.obj" \ + ".\Debug\mark.obj" \ + ".\Debug\mark_rts.obj" \ + ".\Debug\misc.obj" \ + ".\Debug\new_hblk.obj" \ + ".\Debug\obj_map.obj" \ + ".\Debug\os_dep.obj" \ + ".\Debug\ptr_chck.obj" \ + ".\Debug\reclaim.obj" \ + ".\Debug\stubborn.obj" \ + ".\Debug\typd_mlc.obj" \ + ".\Debug\win32_threads.obj" + +".\Debug\gc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gctest - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "gctest\Release" +# PROP BASE Intermediate_Dir "gctest\Release" +# PROP BASE Target_Dir "gctest" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "gctest\Release" +# PROP Intermediate_Dir "gctest\Release" +# PROP Target_Dir "gctest" +OUTDIR=.\gctest\Release +INTDIR=.\gctest\Release + +ALL : "gc - Win32 Release" ".\Release\gctest.exe" + +CLEAN : + -@erase ".\gctest\Release\test.obj" + -@erase ".\Release\gctest.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D\ + "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS"\ + /Fp"$(INTDIR)/gctest.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\gctest\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/gctest.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no\ + /pdb:"$(OUTDIR)/gctest.pdb" /machine:I386 /out:"Release/gctest.exe" +LINK32_OBJS= \ + ".\gctest\Release\test.obj" \ + ".\Release\gc.lib" + +".\Release\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "gctest\Debug" +# PROP BASE Intermediate_Dir "gctest\Debug" +# PROP BASE Target_Dir "gctest" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "gctest\Debug" +# PROP Intermediate_Dir "gctest\Debug" +# PROP Target_Dir "gctest" +OUTDIR=.\gctest\Debug +INTDIR=.\gctest\Debug + +ALL : "gc - Win32 Debug" ".\Debug\gctest.exe" ".\gctest\Debug\gctest.bsc" + +CLEAN : + -@erase ".\Debug\gctest.exe" + -@erase ".\gctest\Debug\gctest.bsc" + -@erase ".\gctest\Debug\gctest.map" + -@erase ".\gctest\Debug\gctest.pdb" + -@erase ".\gctest\Debug\test.obj" + -@erase ".\gctest\Debug\test.sbr" + -@erase ".\gctest\Debug\vc40.idb" + -@erase ".\gctest\Debug\vc40.pdb" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS"\ + /D "ALL_INTERIOR_POINTERS" /D "__STDC__" /D "WIN32_THREADS" /FR"$(INTDIR)/"\ + /Fp"$(INTDIR)/gctest.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\gctest\Debug/ +CPP_SBRS=.\gctest\Debug/ + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/gctest.bsc" +BSC32_SBRS= \ + ".\gctest\Debug\test.sbr" + +".\gctest\Debug\gctest.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /map /debug /machine:I386 /out:"Debug/gctest.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no\ + /pdb:"$(OUTDIR)/gctest.pdb" /map:"$(INTDIR)/gctest.map" /debug /machine:I386\ + /out:"Debug/gctest.exe" +LINK32_OBJS= \ + ".\Debug\gc.lib" \ + ".\gctest\Debug\test.obj" + +".\Debug\gctest.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "cord - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "cord\Release" +# PROP BASE Intermediate_Dir "cord\Release" +# PROP BASE Target_Dir "cord" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "cord\Release" +# PROP Intermediate_Dir "cord\Release" +# PROP Target_Dir "cord" +OUTDIR=.\cord\Release +INTDIR=.\cord\Release + +ALL : "gc - Win32 Release" ".\Release\de.exe" + +CLEAN : + -@erase ".\cord\Release\cordbscs.obj" + -@erase ".\cord\Release\cordxtra.obj" + -@erase ".\cord\Release\de.obj" + -@erase ".\cord\Release\de_win.obj" + -@erase ".\cord\Release\de_win.res" + -@erase ".\Release\de.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c +CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "." /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D\ + "ALL_INTERIOR_POINTERS" /Fp"$(INTDIR)/cord.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\cord\Release/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /win32 +MTL_PROJ=/nologo /D "NDEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"Release/de.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:no /pdb:"$(OUTDIR)/de.pdb"\ + /machine:I386 /out:"Release/de.exe" +LINK32_OBJS= \ + ".\cord\Release\cordbscs.obj" \ + ".\cord\Release\cordxtra.obj" \ + ".\cord\Release\de.obj" \ + ".\cord\Release\de_win.obj" \ + ".\cord\Release\de_win.res" \ + ".\Release\gc.lib" + +".\Release\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "cord\Debug" +# PROP BASE Intermediate_Dir "cord\Debug" +# PROP BASE Target_Dir "cord" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "cord\Debug" +# PROP Intermediate_Dir "cord\Debug" +# PROP Target_Dir "cord" +OUTDIR=.\cord\Debug +INTDIR=.\cord\Debug + +ALL : "gc - Win32 Debug" ".\Debug\de.exe" + +CLEAN : + -@erase ".\cord\Debug\cordbscs.obj" + -@erase ".\cord\Debug\cordxtra.obj" + -@erase ".\cord\Debug\de.obj" + -@erase ".\cord\Debug\de.pdb" + -@erase ".\cord\Debug\de_win.obj" + -@erase ".\cord\Debug\de_win.res" + -@erase ".\cord\Debug\vc40.idb" + -@erase ".\cord\Debug\vc40.pdb" + -@erase ".\Debug\de.exe" + -@erase ".\Debug\de.ilk" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "." /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /YX /c +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /I "." /D "_DEBUG" /D "WIN32" /D\ + "_WINDOWS" /D "ALL_INTERIOR_POINTERS" /Fp"$(INTDIR)/cord.pch" /YX\ + /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c +CPP_OBJS=.\cord\Debug/ +CPP_SBRS=.\. + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +MTL=mktyplib.exe +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /win32 +MTL_PROJ=/nologo /D "_DEBUG" /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +RSC_PROJ=/l 0x809 /fo"$(INTDIR)/de_win.res" /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/cord.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"Debug/de.exe" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\ + odbccp32.lib /nologo /subsystem:windows /incremental:yes\ + /pdb:"$(OUTDIR)/de.pdb" /debug /machine:I386 /out:"Debug/de.exe" +LINK32_OBJS= \ + ".\cord\Debug\cordbscs.obj" \ + ".\cord\Debug\cordxtra.obj" \ + ".\cord\Debug\de.obj" \ + ".\cord\Debug\de_win.obj" \ + ".\cord\Debug\de_win.res" \ + ".\Debug\gc.lib" + +".\Debug\de.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +################################################################################ +# Begin Target + +# Name "gc - Win32 Release" +# Name "gc - Win32 Debug" + +!IF "$(CFG)" == "gc - Win32 Release" + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE=.\reclaim.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_RECLA=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Release\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_RECLA=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_RECLA=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\reclaim.obj" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + +".\Debug\reclaim.sbr" : $(SOURCE) $(DEP_CPP_RECLA) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\os_dep.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_OS_DE=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OS_DE=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + ".\vd\PCR_VD.h"\ + + +".\Release\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + +".\Release\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_OS_DE=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OS_DE=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + ".\vd\PCR_VD.h"\ + + +".\Debug\os_dep.obj" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + +".\Debug\os_dep.sbr" : $(SOURCE) $(DEP_CPP_OS_DE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\misc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MISC_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MISC_=\ + ".\il\PCR_IL.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + +".\Release\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MISC_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MISC_=\ + ".\il\PCR_IL.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\misc.obj" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + +".\Debug\misc.sbr" : $(SOURCE) $(DEP_CPP_MISC_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mark_rts.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MARK_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + +".\Release\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MARK_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mark_rts.obj" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + +".\Debug\mark_rts.sbr" : $(SOURCE) $(DEP_CPP_MARK_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mach_dep.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MACH_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MACH_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + +".\Release\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MACH_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MACH_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mach_dep.obj" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + +".\Debug\mach_dep.sbr" : $(SOURCE) $(DEP_CPP_MACH_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\headers.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_HEADE=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_HEADE=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + +".\Release\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_HEADE=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_HEADE=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\headers.obj" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + +".\Debug\headers.sbr" : $(SOURCE) $(DEP_CPP_HEADE) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\alloc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_ALLOC=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLOC=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + +".\Release\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_ALLOC=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLOC=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\alloc.obj" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + +".\Debug\alloc.sbr" : $(SOURCE) $(DEP_CPP_ALLOC) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\allchblk.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_ALLCH=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLCH=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + +".\Release\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_ALLCH=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_ALLCH=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\allchblk.obj" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + +".\Debug\allchblk.sbr" : $(SOURCE) $(DEP_CPP_ALLCH) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\stubborn.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_STUBB=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_STUBB=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\stubborn.obj" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)" + +".\Release\stubborn.sbr" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_STUBB=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_STUBB=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\stubborn.obj" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)" + +".\Debug\stubborn.sbr" : $(SOURCE) $(DEP_CPP_STUBB) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\obj_map.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_OBJ_M=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OBJ_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + +".\Release\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_OBJ_M=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_OBJ_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\obj_map.obj" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + +".\Debug\obj_map.sbr" : $(SOURCE) $(DEP_CPP_OBJ_M) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\new_hblk.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_NEW_H=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_NEW_H=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + +".\Release\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_NEW_H=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_NEW_H=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\new_hblk.obj" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + +".\Debug\new_hblk.sbr" : $(SOURCE) $(DEP_CPP_NEW_H) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mark.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MARK_C=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + +".\Release\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MARK_C=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MARK_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mark.obj" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + +".\Debug\mark.sbr" : $(SOURCE) $(DEP_CPP_MARK_C) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\malloc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MALLO=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLO=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + +".\Release\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MALLO=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLO=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\malloc.obj" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + +".\Debug\malloc.sbr" : $(SOURCE) $(DEP_CPP_MALLO) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\mallocx.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_MALLX=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLX=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + +".\Release\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_MALLX=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_MALLX=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\mallocx.obj" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + +".\Debug\mallocx.sbr" : $(SOURCE) $(DEP_CPP_MALLX) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\finalize.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_FINAL=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_FINAL=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + +".\Release\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_FINAL=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_FINAL=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\finalize.obj" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + +".\Debug\finalize.sbr" : $(SOURCE) $(DEP_CPP_FINAL) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\dbg_mlc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_DBG_M=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Release\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_DBG_M=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DBG_M=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\dbg_mlc.obj" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + +".\Debug\dbg_mlc.sbr" : $(SOURCE) $(DEP_CPP_DBG_M) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\blacklst.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_BLACK=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_BLACK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + +".\Release\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_BLACK=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_BLACK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\blacklst.obj" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + +".\Debug\blacklst.sbr" : $(SOURCE) $(DEP_CPP_BLACK) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\typd_mlc.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_TYPD_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + ".\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TYPD_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + +".\Release\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_TYPD_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + ".\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TYPD_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\typd_mlc.obj" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + +".\Debug\typd_mlc.sbr" : $(SOURCE) $(DEP_CPP_TYPD_) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\ptr_chck.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_PTR_C=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_PTR_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + +".\Release\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_PTR_C=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_mark.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_PTR_C=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\ptr_chck.obj" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + +".\Debug\ptr_chck.sbr" : $(SOURCE) $(DEP_CPP_PTR_C) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\dyn_load.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_DYN_L=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DYN_L=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + +".\Release\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_DYN_L=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\STAT.H"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_DYN_L=\ + ".\il\PCR_IL.h"\ + ".\mm\PCR_MM.h"\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\dyn_load.obj" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + +".\Debug\dyn_load.sbr" : $(SOURCE) $(DEP_CPP_DYN_L) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\win32_threads.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_WIN32=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Release\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_WIN32=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_WIN32=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\win32_threads.obj" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + +".\Debug\win32_threads.sbr" : $(SOURCE) $(DEP_CPP_WIN32) "$(INTDIR)" + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\checksums.c + +!IF "$(CFG)" == "gc - Win32 Release" + +DEP_CPP_CHECK=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_CHECK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Release\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + +".\Release\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gc - Win32 Debug" + +DEP_CPP_CHECK=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_CHECK=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +".\Debug\checksums.obj" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + +".\Debug\checksums.sbr" : $(SOURCE) $(DEP_CPP_CHECK) "$(INTDIR)" + + +!ENDIF + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "gctest - Win32 Release" +# Name "gctest - Win32 Debug" + +!IF "$(CFG)" == "gctest - Win32 Release" + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "gc" + +!IF "$(CFG)" == "gctest - Win32 Release" + +"gc - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release" + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + +"gc - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug" + +!ENDIF + +# End Project Dependency +################################################################################ +# Begin Source File + +SOURCE=.\test.c +DEP_CPP_TEST_=\ + ".\gcconfig.h"\ + ".\gc.h"\ + ".\gc_hdrs.h"\ + ".\gc_priv.h"\ + ".\gc_typed.h"\ + {$(INCLUDE)}"\sys\TYPES.H"\ + +NODEP_CPP_TEST_=\ + ".\th\PCR_Th.h"\ + ".\th\PCR_ThCrSec.h"\ + ".\th\PCR_ThCtl.h"\ + + +!IF "$(CFG)" == "gctest - Win32 Release" + + +".\gctest\Release\test.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "gctest - Win32 Debug" + + +".\gctest\Debug\test.obj" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + +".\gctest\Debug\test.sbr" : $(SOURCE) $(DEP_CPP_TEST_) "$(INTDIR)" + + +!ENDIF + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "cord - Win32 Release" +# Name "cord - Win32 Debug" + +!IF "$(CFG)" == "cord - Win32 Release" + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +!ENDIF + +################################################################################ +# Begin Project Dependency + +# Project_Dep_Name "gc" + +!IF "$(CFG)" == "cord - Win32 Release" + +"gc - Win32 Release" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Release" + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + +"gc - Win32 Debug" : + $(MAKE) /$(MAKEFLAGS) /F ".\gc.mak" CFG="gc - Win32 Debug" + +!ENDIF + +# End Project Dependency +################################################################################ +# Begin Source File + +SOURCE=.\cord\de_win.c +DEP_CPP_DE_WI=\ + ".\cord\cord.h"\ + ".\cord\de_cmds.h"\ + ".\cord\de_win.h"\ + ".\cord\private\cord_pos.h"\ + +NODEP_CPP_DE_WI=\ + ".\cord\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de_win.obj" : $(SOURCE) $(DEP_CPP_DE_WI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\de.c +DEP_CPP_DE_C2e=\ + ".\cord\cord.h"\ + ".\cord\de_cmds.h"\ + ".\cord\de_win.h"\ + ".\cord\private\cord_pos.h"\ + +NODEP_CPP_DE_C2e=\ + ".\cord\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de.obj" : $(SOURCE) $(DEP_CPP_DE_C2e) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\cordxtra.c +DEP_CPP_CORDX=\ + ".\cord\cord.h"\ + ".\cord\ec.h"\ + ".\cord\private\cord_pos.h"\ + +NODEP_CPP_CORDX=\ + ".\cord\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\cordxtra.obj" : $(SOURCE) $(DEP_CPP_CORDX) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\cordbscs.c +DEP_CPP_CORDB=\ + ".\cord\cord.h"\ + ".\cord\private\cord_pos.h"\ + +NODEP_CPP_CORDB=\ + ".\cord\gc.h"\ + + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\cordbscs.obj" : $(SOURCE) $(DEP_CPP_CORDB) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\cord\de_win.RC + +!IF "$(CFG)" == "cord - Win32 Release" + + +".\cord\Release\de_win.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "NDEBUG" $(SOURCE) + + +!ELSEIF "$(CFG)" == "cord - Win32 Debug" + + +".\cord\Debug\de_win.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)/de_win.res" /i "cord" /d "_DEBUG" $(SOURCE) + + +!ENDIF + +# End Source File +# End Target +# End Project +################################################################################ diff --git a/gc/gc.man b/gc/gc.man new file mode 100644 index 0000000..5409e70 --- /dev/null +++ b/gc/gc.man @@ -0,0 +1,80 @@ +.TH GC_MALLOC 1L "12 February 1996" +.SH NAME +GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement +.SH SYNOPSIS +#include "gc.h" +.br +# define malloc(n) GC_malloc(n) +.br +... malloc(...) ... +.br +.sp +cc ... gc.a +.LP +.SH DESCRIPTION +.I GC_malloc +and +.I GC_free +are plug-in replacements for standard malloc and free. However, +.I +GC_malloc +will attempt to reclaim inaccessible space automatically by invoking a conservative garbage collector at appropriate points. The collector traverses all data structures accessible by following pointers from the machines registers, stack(s), data, and bss segments. Inaccessible structures will be reclaimed. A machine word is considered to be a valid pointer if it is an address inside an object allocated by +.I +GC_malloc +or friends. +.LP +See the documentation in the include file gc_cpp.h for an alternate, C++ specific interface to the garbage collector. +.LP +Unlike the standard implementations of malloc, +.I +GC_malloc +clears the newly allocated storage. +.I +GC_malloc_atomic +does not. Furthermore, it informs the collector that the resulting object will never contain any pointers, and should therefore not be scanned by the collector. +.LP +.I +GC_free +can be used to deallocate objects, but its use is optional, and generally discouraged. +.I +GC_realloc +has the standard realloc semantics. It preserves pointer-free-ness. +.I +GC_register_finalizer +allows for registration of functions that are invoked when an object becomes inaccessible. +.LP +The garbage collector tries to avoid allocating memory at locations that already appear to be referenced before allocation. (Such apparent ``pointers'' are usually large integers and the like that just happen to look like an address.) This may make it hard to allocate very large objects. An attempt to do so may generate a warning. +.LP +.I +GC_malloc_ignore_off_page +and +.I +GC_malloc_atomic_ignore_off_page +inform the collector that the client code will always maintain a pointer to near the beginning of the object (within the first 512 bytes), and that pointers beyond that can be ignored by the collector. This makes it much easier for the collector to place large objects. These are recommended for large object allocation. (Objects expected to be larger than about 100KBytes should be allocated this way.) +.LP +It is also possible to use the collector to find storage leaks in programs destined to be run with standard malloc/free. The collector can be compiled for thread-safe operation. Unlike standard malloc, it is safe to call malloc after a previous malloc call was interrupted by a signal, provided the original malloc call is not resumed. +.LP +The collector may, on rare occasion produce warning messages. On UNIX machines these appear on stderr. Warning messages can be filtered, redirected, or ignored with +.I +GC_set_warn_proc. +This is recommended for production code. See gc.h for details. +.LP +Debugging versions of many of the above routines are provided as macros. Their names are identical to the above, but consist of all capital letters. If GC_DEBUG is defined before gc.h is included, these routines do additional checking, and allow the leak detecting version of the collector to produce slightly more useful output. Without GC_DEBUG defined, they behave exactly like the lower-case versions. +.LP +On some machines, collection will be performed incrementally after a call to +.I +GC_enable_incremental. +This may temporarily write protect pages in the heap. See the README file for more information on how this interacts with system calls that write to the heap. +.LP +Other facilities not discussed here include limited facilities to support incremental collection on machines without appropriate VM support, provisions for providing more explicit object layout information to the garbage collector, more direct support for ``weak'' pointers, support for ``abortable'' garbage collections during idle time, etc. +.LP +.SH "SEE ALSO" +The README and gc.h files in the distribution. More detailed definitions of the functions exported by the collector are given there. (The above list is not complete.) +.LP +Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment", +\fISoftware Practice & Experience\fP, September 1988, pp. 807-820. +.LP +The malloc(3) man page. +.LP +.SH AUTHOR +Hans-J. Boehm (boehm@parc.xerox.com). Some of the code was written by others, most notably Alan Demers. diff --git a/gc/gc_alloc.h b/gc/gc_alloc.h new file mode 100644 index 0000000..1f1d54a --- /dev/null +++ b/gc/gc_alloc.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +// +// This is a C++ header file that is intended to replace the SGI STL +// alloc.h. This assumes SGI STL version < 3.0. +// +// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE +// and -DALL_INTERIOR_POINTERS. We also recommend +// -DREDIRECT_MALLOC=GC_uncollectable_malloc. +// +// Some of this could be faster in the explicit deallocation case. In particular, +// we spend too much time clearing objects on the free lists. That could be avoided. +// +// This uses template classes with static members, and hence does not work +// with g++ 2.7.2 and earlier. +// + +#include "gc.h" + +#ifndef GC_ALLOC_H + +#define GC_ALLOC_H +#define __ALLOC_H // Prevent inclusion of the default version. Ugly. +#define __SGI_STL_ALLOC_H +#define __SGI_STL_INTERNAL_ALLOC_H + +#ifndef __ALLOC +# define __ALLOC alloc +#endif + +#include <stddef.h> +#include <string.h> + +// The following is just replicated from the conventional SGI alloc.h: + +template<class T, class alloc> +class simple_alloc { + +public: + static T *allocate(size_t n) + { return 0 == n? 0 : (T*) alloc::allocate(n * sizeof (T)); } + static T *allocate(void) + { return (T*) alloc::allocate(sizeof (T)); } + static void deallocate(T *p, size_t n) + { if (0 != n) alloc::deallocate(p, n * sizeof (T)); } + static void deallocate(T *p) + { alloc::deallocate(p, sizeof (T)); } +}; + +#include "gc.h" + +// The following need to match collector data structures. +// We can't include gc_priv.h, since that pulls in way too much stuff. +// This should eventually be factored out into another include file. + +extern "C" { + extern void ** const GC_objfreelist_ptr; + extern void ** const GC_aobjfreelist_ptr; + extern void ** const GC_uobjfreelist_ptr; + extern void ** const GC_auobjfreelist_ptr; + + extern void GC_incr_words_allocd(size_t words); + extern void GC_incr_mem_freed(size_t words); + + extern char * GC_generic_malloc_words_small(size_t word, int kind); +} + +// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and +// AUNCOLLECTABLE in gc_priv.h. + +enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2, + GC_AUNCOLLECTABLE = 3 }; + +enum { GC_max_fast_bytes = 255 }; + +enum { GC_bytes_per_word = sizeof(char *) }; + +enum { GC_byte_alignment = 8 }; + +enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word }; + +inline void * &GC_obj_link(void * p) +{ return *(void **)p; } + +// Compute a number of words >= n+1 bytes. +// The +1 allows for pointers one past the end. +inline size_t GC_round_up(size_t n) +{ + return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment; +} + +// The same but don't allow for extra byte. +inline size_t GC_round_up_uncollectable(size_t n) +{ + return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment; +} + +template <int dummy> +class GC_aux_template { +public: + // File local count of allocated words. Occasionally this is + // added into the global count. A separate count is necessary since the + // real one must be updated with a procedure call. + static size_t GC_words_recently_allocd; + + // Same for uncollectable mmory. Not yet reflected in either + // GC_words_recently_allocd or GC_non_gc_bytes. + static size_t GC_uncollectable_words_recently_allocd; + + // Similar counter for explicitly deallocated memory. + static size_t GC_mem_recently_freed; + + // Again for uncollectable memory. + static size_t GC_uncollectable_mem_recently_freed; + + static void * GC_out_of_line_malloc(size_t nwords, int kind); +}; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_mem_recently_freed = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_mem_recently_freed = 0; + +template <int dummy> +void * GC_aux_template<dummy>::GC_out_of_line_malloc(size_t nwords, int kind) +{ + GC_words_recently_allocd += GC_uncollectable_words_recently_allocd; + GC_non_gc_bytes += + GC_bytes_per_word * GC_uncollectable_words_recently_allocd; + GC_uncollectable_words_recently_allocd = 0; + + GC_mem_recently_freed += GC_uncollectable_mem_recently_freed; + GC_non_gc_bytes -= + GC_bytes_per_word * GC_uncollectable_mem_recently_freed; + GC_uncollectable_mem_recently_freed = 0; + + GC_incr_words_allocd(GC_words_recently_allocd); + GC_words_recently_allocd = 0; + + GC_incr_mem_freed(GC_mem_recently_freed); + GC_mem_recently_freed = 0; + + return GC_generic_malloc_words_small(nwords, kind); +} + +typedef GC_aux_template<0> GC_aux; + +// A fast, single-threaded, garbage-collected allocator +// We assume the first word will be immediately overwritten. +// In this version, deallocation is not a noop, and explicit +// deallocation is likely to help performance. +template <int dummy> +class single_client_gc_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc(n); + flh = GC_objfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + flh = GC_aobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_objfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + memset((char *)p + GC_bytes_per_word, 0, + GC_bytes_per_word * (nwords - 1)); + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_aobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_gc_alloc_template<0> single_client_gc_alloc; + +// Once more, for uncollectable objects. +template <int dummy> +class single_client_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + flh = GC_uobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + flh = GC_auobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_uobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_auobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_alloc_template<0> single_client_alloc; + +template < int dummy > +class gc_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic(n); } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } +}; + +typedef gc_alloc_template < 0 > gc_alloc; + +template < int dummy > +class alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic_uncollectable(n); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } +}; + +typedef alloc_template < 0 > alloc; + +#ifdef _SGI_SOURCE + +// We want to specialize simple_alloc so that it does the right thing +// for all pointerfree types. At the moment there is no portable way to +// even approximate that. The following approximation should work for +// SGI compilers, and perhaps some others. + +# define __GC_SPECIALIZE(T,alloc) \ +class simple_alloc<T, alloc> { \ +public: \ + static T *allocate(size_t n) \ + { return 0 == n? 0 : \ + (T*) alloc::ptr_free_allocate(n * sizeof (T)); } \ + static T *allocate(void) \ + { return (T*) alloc::ptr_free_allocate(sizeof (T)); } \ + static void deallocate(T *p, size_t n) \ + { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof (T)); } \ + static void deallocate(T *p) \ + { alloc::ptr_free_deallocate(p, sizeof (T)); } \ +}; + +__GC_SPECIALIZE(char, gc_alloc) +__GC_SPECIALIZE(int, gc_alloc) +__GC_SPECIALIZE(unsigned, gc_alloc) +__GC_SPECIALIZE(float, gc_alloc) +__GC_SPECIALIZE(double, gc_alloc) + +__GC_SPECIALIZE(char, alloc) +__GC_SPECIALIZE(int, alloc) +__GC_SPECIALIZE(unsigned, alloc) +__GC_SPECIALIZE(float, alloc) +__GC_SPECIALIZE(double, alloc) + +__GC_SPECIALIZE(char, single_client_gc_alloc) +__GC_SPECIALIZE(int, single_client_gc_alloc) +__GC_SPECIALIZE(unsigned, single_client_gc_alloc) +__GC_SPECIALIZE(float, single_client_gc_alloc) +__GC_SPECIALIZE(double, single_client_gc_alloc) + +__GC_SPECIALIZE(char, single_client_alloc) +__GC_SPECIALIZE(int, single_client_alloc) +__GC_SPECIALIZE(unsigned, single_client_alloc) +__GC_SPECIALIZE(float, single_client_alloc) +__GC_SPECIALIZE(double, single_client_alloc) + +#ifdef __STL_USE_STD_ALLOCATORS + +???copy stuff from stl_alloc.h or remove it to a different file ??? + +#endif /* __STL_USE_STD_ALLOCATORS */ + +#endif /* _SGI_SOURCE */ + +#endif /* GC_ALLOC_H */ diff --git a/gc/gc_copy_descr.h b/gc/gc_copy_descr.h new file mode 100644 index 0000000..212c99e --- /dev/null +++ b/gc/gc_copy_descr.h @@ -0,0 +1,26 @@ + +/* + * Copyright (c) 1999 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Descriptor for allocation request. May be redefined by client. */ +typedef struct { + GC_word bitmap; /* Bitmap describing pointer locations. */ + /* High order bit correspond to 0th */ + /* word. 2 lsbs must be 0. */ + size_t length; /* In bytes, must be multiple of word */ + /* size. Must be >0, <= 512 */ +} * GC_copy_descriptor; + +/* The collector accesses descriptors only through these two macros. */ +#define GC_SIZE_FROM_DESCRIPTOR(d) ((d) -> length) +#define GC_BIT_MAP_FROM_DESCRIPTOR(d) ((d) -> bitmap) + diff --git a/gc/gc_cpp.cc b/gc/gc_cpp.cc new file mode 100644 index 0000000..547c56f --- /dev/null +++ b/gc/gc_cpp.cc @@ -0,0 +1,60 @@ +/************************************************************************* +Copyright (c) 1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Last modified on Sat Nov 19 19:31:14 PST 1994 by ellis + on Sat Jun 8 15:10:00 PST 1994 by boehm + +Permission is hereby granted to copy this code for any purpose, +provided the above notices are retained on all copies. + +This implementation module for gc_c++.h provides an implementation of +the global operators "new" and "delete" that calls the Boehm +allocator. All objects allocated by this implementation will be +non-collectable but part of the root set of the collector. + +You should ensure (using implementation-dependent techniques) that the +linker finds this module before the library that defines the default +built-in "new" and "delete". + +Authors: John R. Ellis and Jesse Hull + +**************************************************************************/ +/* Boehm, December 20, 1994 7:26 pm PST */ + +#include "gc_cpp.h" + +void* operator new( size_t size ) { + return GC_MALLOC_UNCOLLECTABLE( size );} + +void operator delete( void* obj ) { + GC_FREE( obj );} + +#ifdef _MSC_VER +// This new operator is used by VC++ in case of Debug builds ! +void* operator new( size_t size, + int ,//nBlockUse, + const char * szFileName, + int nLine + ) { +# ifndef GC_DEBUG + return GC_malloc_uncollectable( size ); +# else + return GC_debug_malloc_uncollectable(size, szFileName, nLine); +# endif +} +#endif + +#ifdef OPERATOR_NEW_ARRAY + +void* operator new[]( size_t size ) { + return GC_MALLOC_UNCOLLECTABLE( size );} + +void operator delete[]( void* obj ) { + GC_FREE( obj );} + +#endif /* OPERATOR_NEW_ARRAY */ + + diff --git a/gc/gc_cpp.h b/gc/gc_cpp.h new file mode 100644 index 0000000..ad7df5d --- /dev/null +++ b/gc/gc_cpp.h @@ -0,0 +1,290 @@ +#ifndef GC_CPP_H +#define GC_CPP_H +/**************************************************************************** +Copyright (c) 1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program for any +purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is +granted, provided the above notices are retained, and a notice that +the code was modified is included with the above copyright notice. +**************************************************************************** + +C++ Interface to the Boehm Collector + + John R. Ellis and Jesse Hull + Last modified on Mon Jul 24 15:43:42 PDT 1995 by ellis + +This interface provides access to the Boehm collector. It provides +basic facilities similar to those described in "Safe, Efficient +Garbage Collection for C++", by John R. Elis and David L. Detlefs +(ftp.parc.xerox.com:/pub/ellis/gc). + +All heap-allocated objects are either "collectable" or +"uncollectable". Programs must explicitly delete uncollectable +objects, whereas the garbage collector will automatically delete +collectable objects when it discovers them to be inaccessible. +Collectable objects may freely point at uncollectable objects and vice +versa. + +Objects allocated with the built-in "::operator new" are uncollectable. + +Objects derived from class "gc" are collectable. For example: + + class A: public gc {...}; + A* a = new A; // a is collectable. + +Collectable instances of non-class types can be allocated using the GC +placement: + + typedef int A[ 10 ]; + A* a = new (GC) A; + +Uncollectable instances of classes derived from "gc" can be allocated +using the NoGC placement: + + class A: public gc {...}; + A* a = new (NoGC) A; // a is uncollectable. + +Both uncollectable and collectable objects can be explicitly deleted +with "delete", which invokes an object's destructors and frees its +storage immediately. + +A collectable object may have a clean-up function, which will be +invoked when the collector discovers the object to be inaccessible. +An object derived from "gc_cleanup" or containing a member derived +from "gc_cleanup" has a default clean-up function that invokes the +object's destructors. Explicit clean-up functions may be specified as +an additional placement argument: + + A* a = ::new (GC, MyCleanup) A; + +An object is considered "accessible" by the collector if it can be +reached by a path of pointers from static variables, automatic +variables of active functions, or from some object with clean-up +enabled; pointers from an object to itself are ignored. + +Thus, if objects A and B both have clean-up functions, and A points at +B, B is considered accessible. After A's clean-up is invoked and its +storage released, B will then become inaccessible and will have its +clean-up invoked. If A points at B and B points to A, forming a +cycle, then that's considered a storage leak, and neither will be +collectable. See the interface gc.h for low-level facilities for +handling such cycles of objects with clean-up. + +The collector cannot guarrantee that it will find all inaccessible +objects. In practice, it finds almost all of them. + + +Cautions: + +1. Be sure the collector has been augmented with "make c++". + +2. If your compiler supports the new "operator new[]" syntax, then +add -DOPERATOR_NEW_ARRAY to the Makefile. + +If your compiler doesn't support "operator new[]", beware that an +array of type T, where T is derived from "gc", may or may not be +allocated as a collectable object (it depends on the compiler). Use +the explicit GC placement to make the array collectable. For example: + + class A: public gc {...}; + A* a1 = new A[ 10 ]; // collectable or uncollectable? + A* a2 = new (GC) A[ 10 ]; // collectable + +3. The destructors of collectable arrays of objects derived from +"gc_cleanup" will not be invoked properly. For example: + + class A: public gc_cleanup {...}; + A* a = new (GC) A[ 10 ]; // destructors not invoked correctly + +Typically, only the destructor for the first element of the array will +be invoked when the array is garbage-collected. To get all the +destructors of any array executed, you must supply an explicit +clean-up function: + + A* a = new (GC, MyCleanUp) A[ 10 ]; + +(Implementing clean-up of arrays correctly, portably, and in a way +that preserves the correct exception semantics requires a language +extension, e.g. the "gc" keyword.) + +4. Compiler bugs: + +* Solaris 2's CC (SC3.0) doesn't implement t->~T() correctly, so the +destructors of classes derived from gc_cleanup won't be invoked. +You'll have to explicitly register a clean-up function with +new-placement syntax. + +* Evidently cfront 3.0 does not allow destructors to be explicitly +invoked using the ANSI-conforming syntax t->~T(). If you're using +cfront 3.0, you'll have to comment out the class gc_cleanup, which +uses explicit invocation. + +****************************************************************************/ + +#include "gc.h" + +#ifndef THINK_CPLUS +#define _cdecl +#endif + +#if ! defined( OPERATOR_NEW_ARRAY ) \ + && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6) \ + || __WATCOMC__ >= 1050) +# define OPERATOR_NEW_ARRAY +#endif + +enum GCPlacement {GC, NoGC, PointerFreeGC}; + +class gc {public: + inline void* operator new( size_t size ); + inline void* operator new( size_t size, GCPlacement gcp ); + inline void operator delete( void* obj ); + +#ifdef OPERATOR_NEW_ARRAY + inline void* operator new[]( size_t size ); + inline void* operator new[]( size_t size, GCPlacement gcp ); + inline void operator delete[]( void* obj ); +#endif /* OPERATOR_NEW_ARRAY */ + }; + /* + Instances of classes derived from "gc" will be allocated in the + collected heap by default, unless an explicit NoGC placement is + specified. */ + +class gc_cleanup: virtual public gc {public: + inline gc_cleanup(); + inline virtual ~gc_cleanup(); +private: + inline static void _cdecl cleanup( void* obj, void* clientData );}; + /* + Instances of classes derived from "gc_cleanup" will be allocated + in the collected heap by default. When the collector discovers an + inaccessible object derived from "gc_cleanup" or containing a + member derived from "gc_cleanup", its destructors will be + invoked. */ + +extern "C" {typedef void (*GCCleanUpFunc)( void* obj, void* clientData );} + +inline void* operator new( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup = 0, + void* clientData = 0 ); + /* + Allocates a collectable or uncollected object, according to the + value of "gcp". + + For collectable objects, if "cleanup" is non-null, then when the + allocated object "obj" becomes inaccessible, the collector will + invoke the function "cleanup( obj, clientData )" but will not + invoke the object's destructors. It is an error to explicitly + delete an object allocated with a non-null "cleanup". + + It is an error to specify a non-null "cleanup" with NoGC or for + classes derived from "gc_cleanup" or containing members derived + from "gc_cleanup". */ + +#ifdef OPERATOR_NEW_ARRAY + +inline void* operator new[]( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup = 0, + void* clientData = 0 ); + /* + The operator new for arrays, identical to the above. */ + +#endif /* OPERATOR_NEW_ARRAY */ + +/**************************************************************************** + +Inline implementation + +****************************************************************************/ + +inline void* gc::operator new( size_t size ) { + return GC_MALLOC( size );} + +inline void* gc::operator new( size_t size, GCPlacement gcp ) { + if (gcp == GC) + return GC_MALLOC( size ); + else if (gcp == PointerFreeGC) + return GC_MALLOC_ATOMIC( size ); + else + return GC_MALLOC_UNCOLLECTABLE( size );} + +inline void gc::operator delete( void* obj ) { + GC_FREE( obj );} + + +#ifdef OPERATOR_NEW_ARRAY + +inline void* gc::operator new[]( size_t size ) { + return gc::operator new( size );} + +inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { + return gc::operator new( size, gcp );} + +inline void gc::operator delete[]( void* obj ) { + gc::operator delete( obj );} + +#endif /* OPERATOR_NEW_ARRAY */ + + +inline gc_cleanup::~gc_cleanup() { + GC_REGISTER_FINALIZER_IGNORE_SELF( GC_base(this), 0, 0, 0, 0 );} + +inline void gc_cleanup::cleanup( void* obj, void* displ ) { + ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup();} + +inline gc_cleanup::gc_cleanup() { + GC_finalization_proc oldProc; + void* oldData; + void* base = GC_base( (void *) this ); + if (0 == base) return; + GC_REGISTER_FINALIZER_IGNORE_SELF( + base, cleanup, (void*) ((char*) this - (char*) base), + &oldProc, &oldData ); + if (0 != oldProc) { + GC_REGISTER_FINALIZER_IGNORE_SELF( base, oldProc, oldData, 0, 0 );}} + +inline void* operator new( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup, + void* clientData ) +{ + void* obj; + + if (gcp == GC) { + obj = GC_MALLOC( size ); + if (cleanup != 0) + GC_REGISTER_FINALIZER_IGNORE_SELF( + obj, cleanup, clientData, 0, 0 );} + else if (gcp == PointerFreeGC) { + obj = GC_MALLOC_ATOMIC( size );} + else { + obj = GC_MALLOC_UNCOLLECTABLE( size );}; + return obj;} + + +#ifdef OPERATOR_NEW_ARRAY + +inline void* operator new[]( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup, + void* clientData ) +{ + return ::operator new( size, gcp, cleanup, clientData );} + +#endif /* OPERATOR_NEW_ARRAY */ + + +#endif /* GC_CPP_H */ + diff --git a/gc/gc_hdrs.h b/gc/gc_hdrs.h new file mode 100644 index 0000000..60dc2ad --- /dev/null +++ b/gc/gc_hdrs.h @@ -0,0 +1,135 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, July 11, 1995 11:54 am PDT */ +# ifndef GC_HEADERS_H +# define GC_HEADERS_H +typedef struct hblkhdr hdr; + +# if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 + --> Get a real machine. +# endif + +/* + * The 2 level tree data structure that is used to find block headers. + * If there are more than 32 bits in a pointer, the top level is a hash + * table. + */ + +# if CPP_WORDSZ > 32 +# define HASH_TL +# endif + +/* Define appropriate out-degrees for each of the two tree levels */ +# ifdef SMALL_CONFIG +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +# else +# define LOG_BOTTOM_SZ 10 +# endif +# ifndef HASH_TL +# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +# else +# define LOG_TOP_SZ 11 +# endif +# define TOP_SZ (1 << LOG_TOP_SZ) +# define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) + +typedef struct bi { + hdr * index[BOTTOM_SZ]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* extern bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +# define MAX_JUMP (HBLKSIZE - 1) + +# define HDR_FROM_BI(bi, p) \ + ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)]) +# ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# define GET_BI(p, bottom_indx) (bottom_indx) = BI(p) +# define GET_HDR(p, hhdr) (hhdr) = HDR(p) +# define SET_HDR(p, hhdr) HDR_INNER(p) = (hhdr) +# define GET_HDR_ADDR(p, ha) (ha) = &(HDR_INNER(p)) +# else /* hash */ +/* Hash function for tree top level */ +# define TL_HASH(hi) ((hi) & (TOP_SZ - 1)) +/* Set bottom_indx to point to the bottom index for address p */ +# define GET_BI(p, bottom_indx) \ + { \ + register word hi = \ + (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ + register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ + \ + while (_bi -> key != hi && _bi != GC_all_nils) \ + _bi = _bi -> hash_link; \ + (bottom_indx) = _bi; \ + } +# define GET_HDR_ADDR(p, ha) \ + { \ + register bottom_index * bi; \ + \ + GET_BI(p, bi); \ + (ha) = &(HDR_FROM_BI(bi, p)); \ + } +# define GET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + (hhdr) = *_ha; } +# define SET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + *_ha = (hhdr); } +# define HDR(p) GC_find_header((ptr_t)(p)) +# endif + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NIL? */ +# define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((unsigned long) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +# define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (unsigned long)(hhdr)) +# endif /* GC_HEADERS_H */ diff --git a/gc/gc_mark.h b/gc/gc_mark.h new file mode 100644 index 0000000..4628323 --- /dev/null +++ b/gc/gc_mark.h @@ -0,0 +1,280 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ +/* Boehm, November 7, 1994 4:56 pm PST */ + +/* + * Declarations of mark stack. Needed by marker and client supplied mark + * routines. To be included after gc_priv.h. + */ +#ifndef GC_MARK_H +# define GC_MARK_H + +/* A client supplied mark procedure. Returns new mark stack pointer. */ +/* Primary effect should be to push new entries on the mark stack. */ +/* Mark stack pointer values are passed and returned explicitly. */ +/* Global variables decribing mark stack are not necessarily valid. */ +/* (This usually saves a few cycles by keeping things in registers.) */ +/* Assumed to scan about PROC_BYTES on average. If it needs to do */ +/* much more work than that, it should do it in smaller pieces by */ +/* pushing itself back on the mark stack. */ +/* Note that it should always do some work (defined as marking some */ +/* objects) before pushing more than one entry on the mark stack. */ +/* This is required to ensure termination in the event of mark stack */ +/* overflows. */ +/* This procedure is always called with at least one empty entry on the */ +/* mark stack. */ +/* Currently we require that mark procedures look for pointers in a */ +/* subset of the places the conservative marker would. It must be safe */ +/* to invoke the normal mark procedure instead. */ +# define PROC_BYTES 100 +/* The real declarations of the following are in gc_priv.h, so that */ +/* we can avoid scanning the following table. */ +/* +typedef struct ms_entry * (*mark_proc)( word * addr, mark_stack_ptr, + mark_stack_limit, env ); + +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) +extern mark_proc GC_mark_procs[MAX_MARK_PROCS]; +*/ + +extern word GC_n_mark_procs; + +/* Object descriptors on mark stack or in objects. Low order two */ +/* bits are tags distinguishing among the following 4 possibilities */ +/* for the high order 30 bits. */ +#define DS_TAG_BITS 2 +#define DS_TAGS ((1 << DS_TAG_BITS) - 1) +#define DS_LENGTH 0 /* The entire word is a length in bytes that */ + /* must be a multiple of 4. */ +#define DS_BITMAP 1 /* 30 bits are a bitmap describing pointer */ + /* fields. The msb is 1 iff the first word */ + /* is a pointer. */ + /* (This unconventional ordering sometimes */ + /* makes the marker slightly faster.) */ + /* Zeroes indicate definite nonpointers. Ones */ + /* indicate possible pointers. */ + /* Only usable if pointers are word aligned. */ +# define BITMAP_BITS (WORDSZ - DS_TAG_BITS) +#define DS_PROC 2 + /* The objects referenced by this object can be */ + /* pushed on the mark stack by invoking */ + /* PROC(descr). ENV(descr) is passed as the */ + /* last argument. */ +# define PROC(descr) \ + (GC_mark_procs[((descr) >> DS_TAG_BITS) & (MAX_MARK_PROCS-1)]) +# define ENV(descr) \ + ((descr) >> (DS_TAG_BITS + LOG_MAX_MARK_PROCS)) +# define MAX_ENV \ + (((word)1 << (WORDSZ - DS_TAG_BITS - LOG_MAX_MARK_PROCS)) - 1) +# define MAKE_PROC(proc_index, env) \ + (((((env) << LOG_MAX_MARK_PROCS) | (proc_index)) << DS_TAG_BITS) \ + | DS_PROC) +#define DS_PER_OBJECT 3 /* The real descriptor is at the */ + /* byte displacement from the beginning of the */ + /* object given by descr & ~DS_TAGS */ + +typedef struct ms_entry { + word * mse_start; /* First word of object */ + word mse_descr; /* Descriptor; low order two bits are tags, */ + /* identifying the upper 30 bits as one of the */ + /* following: */ +} mse; + +extern word GC_mark_stack_size; + +extern mse * GC_mark_stack_top; + +extern mse * GC_mark_stack; + +word GC_find_start(); + +mse * GC_signal_mark_stack_overflow(); + +# ifdef GATHERSTATS +# define ADD_TO_ATOMIC(sz) GC_atomic_in_use += (sz) +# define ADD_TO_COMPOSITE(sz) GC_composite_in_use += (sz) +# else +# define ADD_TO_ATOMIC(sz) +# define ADD_TO_COMPOSITE(sz) +# endif + +/* Push the object obj with corresponding heap block header hhdr onto */ +/* the mark stack. */ +# define PUSH_OBJ(obj, hhdr, mark_stack_top, mark_stack_limit) \ +{ \ + register word _descr = (hhdr) -> hb_descr; \ + \ + if (_descr == 0) { \ + ADD_TO_ATOMIC((hhdr) -> hb_sz); \ + } else { \ + ADD_TO_COMPOSITE((hhdr) -> hb_sz); \ + mark_stack_top++; \ + if (mark_stack_top >= mark_stack_limit) { \ + mark_stack_top = GC_signal_mark_stack_overflow(mark_stack_top); \ + } \ + mark_stack_top -> mse_start = (obj); \ + mark_stack_top -> mse_descr = _descr; \ + } \ +} + +#ifdef PRINT_BLACK_LIST +# define GC_FIND_START(current, hhdr, source) \ + GC_find_start(current, hhdr, source) +#else +# define GC_FIND_START(current, hhdr, source) \ + GC_find_start(current, hhdr) +#endif + +/* Push the contents of current onto the mark stack if it is a valid */ +/* ptr to a currently unmarked object. Mark it. */ +/* If we assumed a standard-conforming compiler, we could probably */ +/* generate the exit_label transparently. */ +# define PUSH_CONTENTS(current, mark_stack_top, mark_stack_limit, \ + source, exit_label) \ +{ \ + register int displ; /* Displacement in block; first bytes, then words */ \ + register hdr * hhdr; \ + register map_entry_type map_entry; \ + \ + GET_HDR(current,hhdr); \ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { \ + current = GC_FIND_START(current, hhdr, (word)source); \ + if (current == 0) goto exit_label; \ + hhdr = HDR(current); \ + } \ + displ = HBLKDISPL(current); \ + map_entry = MAP_ENTRY((hhdr -> hb_map), displ); \ + if (map_entry == OBJ_INVALID) { \ + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); goto exit_label; \ + } \ + displ = BYTES_TO_WORDS(displ); \ + displ -= map_entry; \ + \ + { \ + register word * mark_word_addr = hhdr -> hb_marks + divWORDSZ(displ); \ + register word mark_word = *mark_word_addr; \ + register word mark_bit = (word)1 << modWORDSZ(displ); \ + \ + if (mark_word & mark_bit) { \ + /* Mark bit is already set */ \ + goto exit_label; \ + } \ + GC_STORE_BACK_PTR((ptr_t)source, (ptr_t)HBLKPTR(current) \ + + WORDS_TO_BYTES(displ)); \ + *mark_word_addr = mark_word | mark_bit; \ + } \ + PUSH_OBJ(((word *)(HBLKPTR(current)) + displ), hhdr, \ + mark_stack_top, mark_stack_limit) \ + exit_label: ; \ +} + +#ifdef PRINT_BLACK_LIST +# define PUSH_ONE_CHECKED(p, ip, source) \ + GC_push_one_checked(p, ip, (ptr_t)(source)) +#else +# define PUSH_ONE_CHECKED(p, ip, source) \ + GC_push_one_checked(p, ip) +#endif + +/* + * Push a single value onto mark stack. Mark from the object pointed to by p. + * P is considered valid even if it is an interior pointer. + * Previously marked objects are not pushed. Hence we make progress even + * if the mark stack overflows. + */ +# define GC_PUSH_ONE_STACK(p, source) \ + if ((ptr_t)(p) >= GC_least_plausible_heap_addr \ + && (ptr_t)(p) < GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED(p, TRUE, source); \ + } + +/* + * As above, but interior pointer recognition as for + * normal for heap pointers. + */ +# ifdef ALL_INTERIOR_POINTERS +# define AIP TRUE +# else +# define AIP FALSE +# endif +# define GC_PUSH_ONE_HEAP(p,source) \ + if ((ptr_t)(p) >= GC_least_plausible_heap_addr \ + && (ptr_t)(p) < GC_greatest_plausible_heap_addr) { \ + PUSH_ONE_CHECKED(p,AIP,source); \ + } + +/* + * Mark from one finalizable object using the specified + * mark proc. May not mark the object pointed to by + * real_ptr. That is the job of the caller, if appropriate + */ +# define GC_MARK_FO(real_ptr, mark_proc) \ +{ \ + (*(mark_proc))(real_ptr); \ + while (!GC_mark_stack_empty()) GC_mark_from_mark_stack(); \ + if (GC_mark_state != MS_NONE) { \ + GC_set_mark_bit(real_ptr); \ + while (!GC_mark_some((ptr_t)0)); \ + } \ +} + +extern GC_bool GC_mark_stack_too_small; + /* We need a larger mark stack. May be */ + /* set by client supplied mark routines.*/ + +typedef int mark_state_t; /* Current state of marking, as follows:*/ + /* Used to remember where we are during */ + /* concurrent marking. */ + + /* We say something is dirty if it was */ + /* written since the last time we */ + /* retrieved dirty bits. We say it's */ + /* grungy if it was marked dirty in the */ + /* last set of bits we retrieved. */ + + /* Invariant I: all roots and marked */ + /* objects p are either dirty, or point */ + /* to objects q that are either marked */ + /* or a pointer to q appears in a range */ + /* on the mark stack. */ + +# define MS_NONE 0 /* No marking in progress. I holds. */ + /* Mark stack is empty. */ + +# define MS_PUSH_RESCUERS 1 /* Rescuing objects are currently */ + /* being pushed. I holds, except */ + /* that grungy roots may point to */ + /* unmarked objects, as may marked */ + /* grungy objects above scan_ptr. */ + +# define MS_PUSH_UNCOLLECTABLE 2 + /* I holds, except that marked */ + /* uncollectable objects above scan_ptr */ + /* may point to unmarked objects. */ + /* Roots may point to unmarked objects */ + +# define MS_ROOTS_PUSHED 3 /* I holds, mark stack may be nonempty */ + +# define MS_PARTIALLY_INVALID 4 /* I may not hold, e.g. because of M.S. */ + /* overflow. However marked heap */ + /* objects below scan_ptr point to */ + /* marked or stacked objects. */ + +# define MS_INVALID 5 /* I may not hold. */ + +extern mark_state_t GC_mark_state; + +#endif /* GC_MARK_H */ + diff --git a/gc/gc_priv.h b/gc/gc_priv.h new file mode 100644 index 0000000..5ce52a7 --- /dev/null +++ b/gc/gc_priv.h @@ -0,0 +1,1748 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 16, 1996 2:30 pm PST */ + + +# ifndef GC_PRIVATE_H +# define GC_PRIVATE_H + +#if defined(mips) && defined(SYSTYPE_BSD) && defined(sony_news) + /* sony RISC NEWS, NEWSOS 4 */ +# define BSD_TIME +/* typedef long ptrdiff_t; -- necessary on some really old systems */ +#endif + +#if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# define BSD_TIME +#endif + +#ifdef BSD_TIME +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif /* BSD_TIME */ + +# ifndef GC_H +# include "gc.h" +# endif + +typedef GC_word word; +typedef GC_signed_word signed_word; + +# ifndef CONFIG_H +# include "gcconfig.h" +# endif + +# ifndef HEADERS_H +# include "gc_hdrs.h" +# endif + +typedef int GC_bool; +# define TRUE 1 +# define FALSE 0 + +typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements. */ + /* Preferably identical to caddr_t, if it */ + /* exists. */ + +#if defined(__STDC__) +# include <stdlib.h> +# if !(defined( sony_news ) ) +# include <stddef.h> +# endif +# define VOLATILE volatile +# define CONST const +#else +# ifdef MSWIN32 +# include <stdlib.h> +# endif +# define VOLATILE +# define CONST +#endif + +#if 0 /* was once defined for AMIGA */ +# define GC_FAR __far +#else +# define GC_FAR +#endif + +/*********************************/ +/* */ +/* Definitions for conservative */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +#define STUBBORN_ALLOC /* Define stubborn allocation primitives */ +#if defined(SRC_M3) || defined(SMALL_CONFIG) +# undef STUBBORN_ALLOC +#endif + + +/* #define ALL_INTERIOR_POINTERS */ + /* Forces all pointers into the interior of an */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 1. There are probably no interesting, portable, */ + /* strictly ANSI conforming C programs. */ + /* 2. This option makes it hard for the collector */ + /* to allocate space that is not ``pointed to'' */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empiricaly */ + /* observed that it would be difficult to */ + /* allocate individual objects larger than 100K. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* touched. */ + /* If you can easily avoid using this option, do. */ + /* If not, try to keep individual objects small. */ + +#define PRINTSTATS /* Print garbage collection statistics */ + /* For less verbose output, undefine in reclaim.c */ + +#define PRINTTIMES /* Print the amount of time consumed by each garbage */ + /* collection. */ + +#define PRINTBLOCKS /* Print object sizes associated with heap blocks, */ + /* whether the objects are atomic or composite, and */ + /* whether or not the block was found to be empty */ + /* during the reclaim phase. Typically generates */ + /* about one screenful per garbage collection. */ +#undef PRINTBLOCKS + +#ifdef SILENT +# ifdef PRINTSTATS +# undef PRINTSTATS +# endif +# ifdef PRINTTIMES +# undef PRINTTIMES +# endif +# ifdef PRINTNBLOCKS +# undef PRINTNBLOCKS +# endif +#endif + +#if defined(PRINTSTATS) && !defined(GATHERSTATS) +# define GATHERSTATS +#endif + +#ifdef FINALIZE_ON_DEMAND +# define GC_INVOKE_FINALIZERS() +#else +# define GC_INVOKE_FINALIZERS() (void)GC_invoke_finalizers() +#endif + +#define MERGE_SIZES /* Round up some object sizes, so that fewer distinct */ + /* free lists are actually maintained. This applies */ + /* only to the top level routines in misc.c, not to */ + /* user generated code that calls GC_allocobj and */ + /* GC_allocaobj directly. */ + /* Slows down average programs slightly. May however */ + /* substantially reduce fragmentation if allocation */ + /* request sizes are widely scattered. */ + /* May save significant amounts of space for obj_map */ + /* entries. */ + +#ifndef OLD_BLOCK_ALLOC + /* Macros controlling large block allocation strategy. */ +# define EXACT_FIRST /* Make a complete pass through the large object */ + /* free list before splitting a block */ +# define PRESERVE_LAST /* Do not divide last allocated heap segment */ + /* unless we would otherwise need to expand the */ + /* heap. */ +#endif + +/* ALIGN_DOUBLE requires MERGE_SIZES at present. */ +# if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) +# define MERGE_SIZES +# endif + +#if defined(ALL_INTERIOR_POINTERS) && !defined(DONT_ADD_BYTE_AT_END) +# define ADD_BYTE_AT_END +#endif + + +# ifndef LARGE_CONFIG +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ + /* Must be multiple of largest page size. */ +# define MAXHINCR 512 /* Maximum heap increment, in blocks */ +# else +# define MINHINCR 64 +# define MAXHINCR 4096 +# endif + +# define TIME_LIMIT 50 /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef SAVE_CALL_CHAIN + +/* + * Number of frames and arguments to save in objects allocated by + * debugging allocator. + */ +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# define NARGS 2 /* Mumber of arguments to save for each call. */ + +# define NEED_CALLINFO + +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ +void GC_save_callers (/* struct callinfo info[NFRAMES] */); + +void GC_print_callers (/* struct callinfo info[NFRAMES] */); + +#else + +# ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +# endif + +#endif + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if defined(ALIGN_DOUBLE) && (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; +#endif + + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define GET_TIME(x) { struct rusage rusage; \ + getrusage (RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; } +# define MS_TIME_DIFF(a,b) ((double) (a.tv_sec - b.tv_sec) * 1000.0 \ + + (double) (a.tv_usec - b.tv_usec) / 1000.0) +#else /* !BSD_TIME */ +# include <time.h> +# if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4) + clock_t clock(); /* Not in time.h, where it belongs */ +# endif +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include <machine/limits.h> +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 +/* + * This is technically a bug in the implementation. ANSI requires that + * CLOCKS_PER_SEC be defined. But at least under SunOS4.1.1, it isn't. + * Also note that the combination of ANSI C and POSIX is incredibly gross + * here. The type clock_t is used by both clock() and times(). But on + * some machines these use different notions of a clock tick, CLOCKS_PER_SEC + * seems to apply only to clock. Hence we use it here. On many machines, + * including SunOS, clock actually uses units of microseconds (which are + * not really clock ticks). + */ +# endif +# define CLOCK_TYPE clock_t +# define GET_TIME(x) x = clock() +# define MS_TIME_DIFF(a,b) ((unsigned long) \ + (1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC)) +#endif /* !BSD_TIME */ + +/* We use bzero and bcopy internally. They may not be available. */ +# if defined(SPARC) && defined(SUNOS4) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(AMIGA) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(NEXT) +# define BCOPY_EXISTS +# endif +# if defined(VAX) +# define BCOPY_EXISTS +# endif +# if defined(AMIGA) +# include <string.h> +# define BCOPY_EXISTS +# endif + +# ifndef BCOPY_EXISTS +# include <string.h> +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((char *)(x),(char *)(y),(int)(n)) +# define BZERO(x,n) bzero((char *)(x),(int)(n)) +# endif + +/* HBLKSIZE aligned allocation. 0 is taken to mean failure */ +/* space is assumed to be cleared. */ +/* In the case os USE_MMAP, the argument must also be a */ +/* physical page size. */ +/* GET_MEM is currently not assumed to retrieve 0 filled space, */ +/* though we should perhaps take advantage of the case in which */ +/* does. */ +# ifdef PCR + char * real_malloc(); +# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef OS2 + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ + + GC_page_size) \ + + GC_page_size-1) +# else +# if defined(AMIGA) || defined(NEXT) || defined(MACOSX) || defined(DOS4GW) +# define GET_MEM(bytes) HBLKPTR((size_t) \ + calloc(1, (size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef MSWIN32 + extern ptr_t GC_win32_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# else +# ifdef MACOS +# if defined(USE_TEMPORARY_MEMORY) + extern Ptr GC_MacTemporaryNewPtr(size_t size, + Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR( \ + GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR( \ + NewPtrClear(bytes + GC_page_size) + GC_page_size-1) +# endif +# else + extern ptr_t GC_unix_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +# endif +# endif +# endif +# endif + +/* + * Mutual exclusion between allocator/collector routines. + * Needed if there is more than one allocator thread. + * FASTLOCK() is assumed to try to acquire the lock in a cheap and + * dirty way that is acceptable for a few instructions, e.g. by + * inhibiting preemption. This is assumed to have succeeded only + * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE. + * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED(). + * If signals cannot be tolerated with the FASTLOCK held, then + * FASTLOCK should disable signals. The code executed under + * FASTLOCK is otherwise immune to interruption, provided it is + * not restarted. + * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK + * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK. + * (There is currently no equivalent for FASTLOCK.) + */ +# ifdef THREADS +# ifdef PCR_OBSOLETE /* Faster, but broken with multiple lwp's */ +# include "th/PCR_Th.h" +# include "th/PCR_ThCrSec.h" + extern struct PCR_Th_MLRep GC_allocate_ml; +# define DCL_LOCK_STATE PCR_sigset_t GC_old_sig_mask +# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# define FASTLOCK() PCR_ThCrSec_EnterSys() + /* Here we cheat (a lot): */ +# define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0) + /* TRUE if nobody currently holds the lock */ +# define FASTUNLOCK() PCR_ThCrSec_ExitSys() +# endif +# ifdef PCR +# include <base/PCR_Base.h> +# include <th/PCR_Th.h> + extern PCR_Th_ML GC_allocate_ml; +# define DCL_LOCK_STATE \ + PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask +# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml)) +# define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay) +# define FASTUNLOCK() {\ + if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); } +# endif +# ifdef SRC_M3 + extern word RT0u__inCritical; +# define LOCK() RT0u__inCritical++ +# define UNLOCK() RT0u__inCritical-- +# endif +# ifdef SOLARIS_THREADS +# include <thread.h> +# include <signal.h> + extern mutex_t GC_allocate_ml; +# define LOCK() mutex_lock(&GC_allocate_ml); +# define UNLOCK() mutex_unlock(&GC_allocate_ml); +# endif +# ifdef LINUX_THREADS +# include <pthread.h> +# ifdef __i386__ + inline static int GC_test_and_set(volatile unsigned int *addr) { + int oldval; + /* Note: the "xchg" instruction does not need a "lock" prefix */ + __asm__ __volatile__("xchgl %0, %1" + : "=r"(oldval), "=m"(*(addr)) + : "0"(1), "m"(*(addr))); + return oldval; + } +# else + -- > Need implementation of GC_test_and_set() +# endif +# define GC_clear(addr) (*(addr) = 0) + + extern volatile unsigned int GC_allocate_lock; + /* This is not a mutex because mutexes that obey the (optional) */ + /* POSIX scheduling rules are subject to convoys in high contention */ + /* applications. This is basically a spin lock. */ + extern pthread_t GC_lock_holder; + extern void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +# define NO_THREAD (pthread_t)(-1) +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +# ifdef UNDEFINED +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# define LOCK() \ + { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); } +# define UNLOCK() \ + GC_clear(&GC_allocate_lock) +# endif + extern GC_bool GC_collecting; +# define ENTER_GC() \ + { \ + GC_collecting = 1; \ + } +# define EXIT_GC() GC_collecting = 0; +# endif /* LINUX_THREADS */ +# if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) +# include <pthread.h> +# include <mutex.h> + +# if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ + || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 +# define GC_test_and_set(addr, v) test_and_set(addr,v) +# else +# define GC_test_and_set(addr, v) __test_and_set(addr,v) +# endif + extern unsigned long GC_allocate_lock; + /* This is not a mutex because mutexes that obey the (optional) */ + /* POSIX scheduling rules are subject to convoys in high contention */ + /* applications. This is basically a spin lock. */ + extern pthread_t GC_lock_holder; + extern void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +# define NO_THREAD (pthread_t)(-1) +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +# ifdef UNDEFINED +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); } +# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \ + && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 700 +# define UNLOCK() __lock_release(&GC_allocate_lock) +# else + /* The function call in the following should prevent the */ + /* compiler from moving assignments to below the UNLOCK. */ + /* This is probably not necessary for ucode or gcc 2.8. */ + /* It may be necessary for Ragnarok and future gcc */ + /* versions. */ +# define UNLOCK() { GC_noop1(&GC_allocate_lock); \ + *(volatile unsigned long *)(&GC_allocate_lock) = 0; } +# endif +# endif + extern GC_bool GC_collecting; +# define ENTER_GC() \ + { \ + GC_collecting = 1; \ + } +# define EXIT_GC() GC_collecting = 0; +# endif /* IRIX_THREADS || IRIX_JDK_THREADS */ +# ifdef WIN32_THREADS +# include <windows.h> + GC_API CRITICAL_SECTION GC_allocate_ml; +# define LOCK() EnterCriticalSection(&GC_allocate_ml); +# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# endif +# ifndef SET_LOCK_HOLDER +# define SET_LOCK_HOLDER() +# define UNSET_LOCK_HOLDER() +# define I_HOLD_LOCK() FALSE + /* Used on platforms were locks can be reacquired, */ + /* so it doesn't matter if we lie. */ +# endif +# else +# define LOCK() +# define UNLOCK() +# endif +# ifndef SET_LOCK_HOLDER +# define SET_LOCK_HOLDER() +# define UNSET_LOCK_HOLDER() +# define I_HOLD_LOCK() FALSE + /* Used on platforms were locks can be reacquired, */ + /* so it doesn't matter if we lie. */ +# endif +# ifndef ENTER_GC +# define ENTER_GC() +# define EXIT_GC() +# endif + +# ifndef DCL_LOCK_STATE +# define DCL_LOCK_STATE +# endif +# ifndef FASTLOCK +# define FASTLOCK() LOCK() +# define FASTLOCK_SUCCEEDED() TRUE +# define FASTUNLOCK() UNLOCK() +# endif + +/* Delay any interrupts or signals that may abort this thread. Data */ +/* structures are in a consistent state outside this pair of calls. */ +/* ANSI C allows both to be empty (though the standard isn't very */ +/* clear on that point). Standard malloc implementations are usually */ +/* neither interruptable nor thread-safe, and thus correspond to */ +/* empty definitions. */ +# ifdef PCR +# define DISABLE_SIGNALS() \ + PCR_Th_SetSigMask(PCR_allSigsBlocked,&GC_old_sig_mask) +# define ENABLE_SIGNALS() \ + PCR_Th_SetSigMask(&GC_old_sig_mask, NIL) +# else +# if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ + || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ + || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ + || defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS) + /* Also useful for debugging. */ + /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ +# define DISABLE_SIGNALS() +# define ENABLE_SIGNALS() +# else +# define DISABLE_SIGNALS() GC_disable_signals() + void GC_disable_signals(); +# define ENABLE_SIGNALS() GC_enable_signals() + void GC_enable_signals(); +# endif +# endif + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# include "th/PCR_ThCtl.h" +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever); +# else +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) + void GC_stop_world(); + void GC_start_world(); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else +# define STOP_WORLD() +# define START_WORLD() +# endif +# endif + +/* Abandon ship */ +# ifdef PCR +# define ABORT(s) PCR_Base_Panic(s) +# else +# ifdef SMALL_CONFIG +# define ABORT(msg) abort(); +# else + GC_API void GC_abort(); +# define ABORT(msg) GC_abort(msg); +# endif +# endif + +/* Exit abnormally, but without making a mess (e.g. out of memory) */ +# ifdef PCR +# define EXIT() PCR_Base_Exit(1,PCR_waitForever) +# else +# define EXIT() (void)exit(1) +# endif + +/* Print warning message, e.g. almost out of memory. */ +# define WARN(msg,arg) (*GC_current_warn_proc)(msg, (GC_word)(arg)) +extern GC_warn_proc GC_current_warn_proc; + +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +#if CPP_WORDSZ == 32 +# define WORDS_TO_BYTES(x) ((x)<<2) +# define BYTES_TO_WORDS(x) ((x)>>2) +# define LOGWL ((word)5) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */ +# if ALIGNMENT != 4 +# define UNALIGNED +# endif +#endif + +#if CPP_WORDSZ == 64 +# define WORDS_TO_BYTES(x) ((x)<<3) +# define BYTES_TO_WORDS(x) ((x)>>3) +# define LOGWL ((word)6) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */ +# if ALIGNMENT != 8 +# define UNALIGNED +# endif +#endif + +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1 << (WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define ONES ((word)(-1)) +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* heap block size, bytes. Should be power of 2 */ + +#ifndef HBLKSIZE +# ifdef SMALL_CONFIG +# define CPP_LOG_HBLKSIZE 10 +# else +# if CPP_WORDSZ == 32 +# define CPP_LOG_HBLKSIZE 12 +# else +# define CPP_LOG_HBLKSIZE 13 +# endif +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# endif +# if HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# endif +# if HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# endif +# if HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# endif +# if HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# endif +# if HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# endif +# ifndef CPP_LOG_HBLKSIZE + --> fix HBLKSIZE +# endif +# undef HBLKSIZE +#endif +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((word)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((word)CPP_HBLKSIZE) + + +/* max size objects supported by freelist (larger objects may be */ +/* allocated, but less efficiently) */ + +#define CPP_MAXOBJSZ BYTES_TO_WORDS(CPP_HBLKSIZE/2) +#define MAXOBJSZ ((word)CPP_MAXOBJSZ) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word) (objptr)) & ~(HBLKSIZE-1))) + +# define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1)) + +/* Round up byte allocation requests to integral number of words, etc. */ +# ifdef ADD_BYTE_AT_END +# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1)) +# ifdef ALIGN_DOUBLE +# define ALIGNED_WORDS(n) (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2)) & ~1) +# else +# define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) < WORDS_TO_BYTES(MAXOBJSZ)) +# define ADD_SLOP(bytes) ((bytes)+1) +# else +# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1)) +# ifdef ALIGN_DOUBLE +# define ALIGNED_WORDS(n) \ + (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1) & ~1) +# else +# define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) <= WORDS_TO_BYTES(MAXOBJSZ)) +# define ADD_SLOP(bytes) (bytes) +# endif + + +/* + * Hash table representation of sets of pages. This assumes it is + * OK to add spurious entries to sets. + * Used by black-listing code, and perhaps by dirty bit maintenance code. + */ + +# ifdef LARGE_CONFIG +# define LOG_PHT_ENTRIES 17 +# else +# define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */ + /* to more than 16K hblks = 64MB. */ + /* Each hash table occupies 2K bytes. */ +# endif +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (PHT_ENTRIES >> LOGWL) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) +# define clear_pht_entry_from_index(bl, index) \ + (bl)[divWORDSZ(index)] &= ~((word)1 << modWORDSZ(index)) + + + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +/* heap block header */ +#define HBLKMASK (HBLKSIZE-1) + +#define BITS_PER_HBLK (HBLKSIZE * 8) + +#define MARK_BITS_PER_HBLK (BITS_PER_HBLK/CPP_WORDSZ) + /* upper bound */ + /* We allocate 1 bit/word. Only the first word */ + /* in each object is actually marked. */ + +# ifdef ALIGN_DOUBLE +# define MARK_BITS_SZ (((MARK_BITS_PER_HBLK + 2*CPP_WORDSZ - 1) \ + / (2*CPP_WORDSZ))*2) +# else +# define MARK_BITS_SZ ((MARK_BITS_PER_HBLK + CPP_WORDSZ - 1)/CPP_WORDSZ) +# endif + /* Upper bound on number of mark words per heap block */ + +struct hblkhdr { + word hb_sz; /* If in use, size in words, of objects in the block. */ + /* if free, the size in bytes of the whole block */ + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + word hb_descr; /* object descriptor for marking. See */ + /* mark.h. */ + char* hb_map; /* A pointer to a pointer validity map of the block. */ + /* See GC_obj_map. */ + /* Valid for all blocks with headers. */ + /* Free blocks point to GC_invalid_map. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first page of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* unly for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ + word hb_marks[MARK_BITS_SZ]; + /* Bit i in the array refers to the */ + /* object starting at the ith word (header */ + /* INCLUDED) in the heap block. */ + /* The lsb of word 0 is numbered 0. */ +}; + +/* heap block body */ + +# define DISCARD_WORDS 0 + /* Number of words to be dropped at the beginning of each block */ + /* Must be a multiple of WORDSZ. May reasonably be nonzero */ + /* on machines that don't guarantee longword alignment of */ + /* pointers, so that the number of false hits is minimized. */ + /* 0 and WORDSZ are probably the only reasonable values. */ + +# define BODY_SZ ((HBLKSIZE-WORDS_TO_BYTES(DISCARD_WORDS))/sizeof(word)) + +struct hblk { +# if (DISCARD_WORDS != 0) + word garbage[DISCARD_WORDS]; +# endif + word hb_body[BODY_SZ]; +}; + +# define HDR_WORDS ((word)DISCARD_WORDS) +# define HDR_BYTES ((word)WORDS_TO_BYTES(DISCARD_WORDS)) + +# define OBJ_SZ_TO_BLOCKS(sz) \ + divHBLKSZ(HDR_BYTES + WORDS_TO_BYTES(sz) + HBLKSIZE-1) + /* Size of block (in units of HBLKSIZE) needed to hold objects of */ + /* given sz (in words). */ + +/* Object free list link */ +# define obj_link(p) (*(ptr_t *)(p)) + +/* The type of mark procedures. This really belongs in gc_mark.h. */ +/* But we put it here, so that we can avoid scanning the mark proc */ +/* table. */ +typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, + mark_stack_limit, env */); +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 4096 +# else +# ifdef PCR +# define MAX_ROOT_SETS 1024 +# else +# ifdef MSWIN32 +# define MAX_ROOT_SETS 512 + /* Under NT, we add only written pages, which can result */ + /* in many small root sets. */ +# else +# define MAX_ROOT_SETS 64 +# endif +# endif +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start; + ptr_t r_end; +# ifndef MSWIN32 + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#ifndef MSWIN32 + /* Size of hash table index to roots. */ +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +#endif + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inl.h will work even if a different set */ +/* of macros is defined when the client is */ +/* compiled. */ + +struct _GC_arrays { + word _heapsize; + word _max_heapsize; + ptr_t _last_heap_addr; + ptr_t _prev_heap_addr; + word _large_free_bytes; + /* Total bytes contained in blocks on large object free */ + /* list. */ + word _words_allocd_before_gc; + /* Number of words allocated before this */ + /* collection cycle. */ + word _words_allocd; + /* Number of words allocated during this collection cycle */ + word _words_wasted; + /* Number of words wasted due to internal fragmentation */ + /* in large objects, or due to dropping blacklisted */ + /* blocks, since last gc. Approximate. */ + word _words_finalized; + /* Approximate number of words in objects (and headers) */ + /* That became ready for finalization in the last */ + /* collection. */ + word _non_gc_bytes_at_gc; + /* Number of explicitly managed bytes of storage */ + /* at last collection. */ + word _mem_freed; + /* Number of explicitly deallocated words of memory */ + /* since last collection. */ + mark_proc _mark_procs[MAX_MARK_PROCS]; + /* Table of user-defined mark procedures. There is */ + /* a small number of these, which can be referenced */ + /* by DS_PROC mark descriptors. See gc_mark.h. */ + ptr_t _objfreelist[MAXOBJSZ+1]; + /* free list for objects */ + ptr_t _aobjfreelist[MAXOBJSZ+1]; + /* free list for atomic objs */ + + ptr_t _uobjfreelist[MAXOBJSZ+1]; + /* uncollectable but traced objs */ + /* objects on this and auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef ATOMIC_UNCOLLECTABLE + ptr_t _auobjfreelist[MAXOBJSZ+1]; +# endif + /* uncollectable but traced objs */ + +# ifdef GATHERSTATS + word _composite_in_use; + /* Number of words in accessible composite */ + /* objects. */ + word _atomic_in_use; + /* Number of words in accessible atomic */ + /* objects. */ +# endif +# ifdef USE_MUNMAP + word _unmapped_bytes; +# endif +# ifdef MERGE_SIZES + unsigned _size_map[WORDS_TO_BYTES(MAXOBJSZ+1)]; + /* Number of words to allocate for a given allocation request in */ + /* bytes. */ +# endif + +# ifdef STUBBORN_ALLOC + ptr_t _sobjfreelist[MAXOBJSZ+1]; +# endif + /* free list for immutable objects */ + ptr_t _obj_map[MAXOBJSZ+1]; + /* If not NIL, then a pointer to a map of valid */ + /* object addresses. _obj_map[sz][i] is j if the */ + /* address block_start+i is a valid pointer */ + /* to an object at */ + /* block_start+i&~3 - WORDS_TO_BYTES(j). */ + /* (If ALL_INTERIOR_POINTERS is defined, then */ + /* instead ((short *)(hb_map[sz])[i] is j if */ + /* block_start+WORDS_TO_BYTES(i) is in the */ + /* interior of an object starting at */ + /* block_start+WORDS_TO_BYTES(i-j)). */ + /* It is OBJ_INVALID if */ + /* block_start+WORDS_TO_BYTES(i) is not */ + /* valid as a pointer to an object. */ + /* We assume all values of j <= OBJ_INVALID. */ + /* The zeroth entry corresponds to large objects.*/ +# ifdef ALL_INTERIOR_POINTERS +# define map_entry_type short +# define OBJ_INVALID 0x7fff +# define MAP_ENTRY(map, bytes) \ + (((map_entry_type *)(map))[BYTES_TO_WORDS(bytes)]) +# define MAP_ENTRIES BYTES_TO_WORDS(HBLKSIZE) +# define MAP_SIZE (MAP_ENTRIES * sizeof(map_entry_type)) +# define OFFSET_VALID(displ) TRUE +# define CPP_MAX_OFFSET (HBLKSIZE - HDR_BYTES - 1) +# define MAX_OFFSET ((word)CPP_MAX_OFFSET) +# else +# define map_entry_type char +# define OBJ_INVALID 0x7f +# define MAP_ENTRY(map, bytes) \ + (map)[bytes] +# define MAP_ENTRIES HBLKSIZE +# define MAP_SIZE MAP_ENTRIES +# define CPP_MAX_OFFSET (WORDS_TO_BYTES(OBJ_INVALID) - 1) +# define MAX_OFFSET ((word)CPP_MAX_OFFSET) +# define VALID_OFFSET_SZ \ + (CPP_MAX_OFFSET > WORDS_TO_BYTES(CPP_MAXOBJSZ)? \ + CPP_MAX_OFFSET+1 \ + : WORDS_TO_BYTES(CPP_MAXOBJSZ)+1) + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ +# define OFFSET_VALID(displ) GC_valid_offsets[displ] + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# endif +# ifdef STUBBORN_ALLOC + page_hash_table _changed_pages; + /* Stubborn object pages that were changes since last call to */ + /* GC_read_changed. */ + page_hash_table _prev_changed_pages; + /* Stubborn object pages that were changes before last call to */ + /* GC_read_changed. */ +# endif +# if defined(PROC_VDB) || defined(MPROTECT_VDB) + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# endif +# ifdef MPROTECT_VDB + VOLATILE page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# ifdef PROC_VDB + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 4096 /* overflows at roughly 64 GB */ +# else +# define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ +# endif +# else +# define MAX_HEAP_SECTS 256 +# endif + struct HeapSect { + ptr_t hs_start; word hs_bytes; + } _heap_sects[MAX_HEAP_SECTS]; +# ifdef MSWIN32 + ptr_t _heap_bases[MAX_HEAP_SECTS]; + /* Start address of memory regions obtained from kernel. */ +# endif + struct roots _static_roots[MAX_ROOT_SETS]; +# ifndef MSWIN32 + struct roots * _root_index[RT_SIZE]; +# endif + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _all_nils; + bottom_index * _top_index [TOP_SZ]; +#ifdef SAVE_CALL_CHAIN + struct callinfo _last_stack[NFRAMES]; /* Stack at last garbage collection.*/ + /* Useful for debugging mysterious */ + /* object disappearances. */ + /* In the multithreaded case, we */ + /* currently only save the calling */ + /* stack. */ +#endif +}; + +GC_API GC_FAR struct _GC_arrays GC_arrays; + +# define GC_objfreelist GC_arrays._objfreelist +# define GC_aobjfreelist GC_arrays._aobjfreelist +# define GC_uobjfreelist GC_arrays._uobjfreelist +# ifdef ATOMIC_UNCOLLECTABLE +# define GC_auobjfreelist GC_arrays._auobjfreelist +# endif +# define GC_sobjfreelist GC_arrays._sobjfreelist +# define GC_valid_offsets GC_arrays._valid_offsets +# define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +# ifdef STUBBORN_ALLOC +# define GC_changed_pages GC_arrays._changed_pages +# define GC_prev_changed_pages GC_arrays._prev_changed_pages +# endif +# define GC_obj_map GC_arrays._obj_map +# define GC_last_heap_addr GC_arrays._last_heap_addr +# define GC_prev_heap_addr GC_arrays._prev_heap_addr +# define GC_words_allocd GC_arrays._words_allocd +# define GC_words_wasted GC_arrays._words_wasted +# define GC_large_free_bytes GC_arrays._large_free_bytes +# define GC_words_finalized GC_arrays._words_finalized +# define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc +# define GC_mem_freed GC_arrays._mem_freed +# define GC_mark_procs GC_arrays._mark_procs +# define GC_heapsize GC_arrays._heapsize +# define GC_max_heapsize GC_arrays._max_heapsize +# define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc +# define GC_heap_sects GC_arrays._heap_sects +# define GC_last_stack GC_arrays._last_stack +# ifdef USE_MUNMAP +# define GC_unmapped_bytes GC_arrays._unmapped_bytes +# endif +# ifdef MSWIN32 +# define GC_heap_bases GC_arrays._heap_bases +# endif +# define GC_static_roots GC_arrays._static_roots +# define GC_root_index GC_arrays._root_index +# define GC_excl_table GC_arrays._excl_table +# define GC_all_nils GC_arrays._all_nils +# define GC_top_index GC_arrays._top_index +# if defined(PROC_VDB) || defined(MPROTECT_VDB) +# define GC_grungy_pages GC_arrays._grungy_pages +# endif +# ifdef MPROTECT_VDB +# define GC_dirty_pages GC_arrays._dirty_pages +# endif +# ifdef PROC_VDB +# define GC_written_pages GC_arrays._written_pages +# endif +# ifdef GATHERSTATS +# define GC_composite_in_use GC_arrays._composite_in_use +# define GC_atomic_in_use GC_arrays._atomic_in_use +# endif +# ifdef MERGE_SIZES +# define GC_size_map GC_arrays._size_map +# endif + +# define beginGC_arrays ((ptr_t)(&GC_arrays)) +# define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) + +/* Object kinds: */ +# define MAXOBJKINDS 16 + +extern struct obj_kind { + ptr_t *ok_freelist; /* Array of free listheaders for this kind of object */ + /* Point either to GC_arrays or to storage allocated */ + /* with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to be */ + /* swept. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; /* Clear objects before putting them on the free list. */ +} GC_obj_kinds[MAXOBJKINDS]; + +# define endGC_obj_kinds (((ptr_t)(&GC_obj_kinds)) + (sizeof GC_obj_kinds)) + +# define end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ + endGC_obj_kinds : endGC_arrays) + +/* Predefined kinds: */ +# define PTRFREE 0 +# define NORMAL 1 +# define UNCOLLECTABLE 2 +# ifdef ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define STUBBORN 4 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# else +# define STUBBORN 3 +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# endif + +extern int GC_n_kinds; + +GC_API word GC_fo_entries; + +extern word GC_n_heap_sects; /* Number of separately added heap */ + /* sections. */ + +extern word GC_page_size; + +# ifdef MSWIN32 +extern word GC_n_heap_bases; /* See GC_heap_bases. */ +# endif + +extern word GC_total_stack_black_listed; + /* Number of bytes on stack blacklist. */ + +extern word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +extern char * GC_invalid_map; + /* Pointer to the nowhere valid hblk map */ + /* Blocks pointing to this map are free. */ + +extern struct hblk * GC_hblkfreelist[]; + /* List of completely empty heap blocks */ + /* Linked through hb_next field of */ + /* header structure associated with */ + /* block. */ + +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ + +extern GC_bool GC_objects_are_marked; /* There are marked objects in */ + /* the heap. */ + +#ifndef SMALL_CONFIG + extern GC_bool GC_incremental; + /* Using incremental/generational collection. */ +#else +# define GC_incremental TRUE + /* Hopefully allow optimizer to remove some code. */ +#endif + +extern GC_bool GC_dirty_maintained; + /* Dirty bits are being maintained, */ + /* either for incremental collection, */ + /* or to limit the root set. */ + +extern word GC_root_size; /* Total size of registered root sections */ + +extern GC_bool GC_debugging_started; /* GC_debug_malloc has been called. */ + +extern ptr_t GC_least_plausible_heap_addr; +extern ptr_t GC_greatest_plausible_heap_addr; + /* Bounds on the heap. Guaranteed valid */ + /* Likely to include future heap expansion. */ + +/* Operations */ +# ifndef abs +# define abs(x) ((x) < 0? (-(x)) : (x)) +# endif + + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* with it. Only those corresponding to the beginning of an */ +/* object are used. */ + + +/* Mark bit operations */ + +/* + * Retrieve, set, clear the mark bit corresponding + * to the nth word in a given heap block. + * + * (Recall that bit n corresponds to object beginning at word n + * relative to the beginning of the block, including unused words) + */ + +# define mark_bit_from_hdr(hhdr,n) (((hhdr)->hb_marks[divWORDSZ(n)] \ + >> (modWORDSZ(n))) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ + |= (word)1 << modWORDSZ(n) + +# define clear_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ + &= ~((word)1 << modWORDSZ(n)) + +/* Important internal collector routines */ + +ptr_t GC_approx_sp(); + +GC_bool GC_should_collect(); +#ifdef PRESERVE_LAST + GC_bool GC_in_last_heap_sect(/* ptr_t */); + /* In last added heap section? If so, avoid breaking up. */ +#endif +void GC_apply_to_all_blocks(/*fn, client_data*/); + /* Invoke fn(hbp, client_data) for each */ + /* allocated heap block. */ +struct hblk * GC_next_used_block(/* struct hblk * h */); + /* Return first in-use block >= h */ +struct hblk * GC_prev_block(/* struct hblk * h */); + /* Return last block <= h. Returned block */ + /* is managed by GC, but may or may not be in */ + /* use. */ +void GC_mark_init(); +void GC_clear_marks(); /* Clear mark bits for all heap objects. */ +void GC_invalidate_mark_state(); /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. */ + /* Reset mark stack. */ +void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ + /* Return after about one pages worth of */ + /* work. */ +GC_bool GC_mark_stack_empty(); +GC_bool GC_mark_some(/* cold_gc_frame */); + /* Perform about one pages worth of marking */ + /* work of whatever kind is needed. Returns */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +void GC_initiate_gc(); /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full colleection. Otherwise */ + /* it's partial. */ +void GC_push_all(/*b,t*/); /* Push everything in a range */ + /* onto mark stack. */ +void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ + /* subintervals of [b,t) onto */ + /* mark stack. */ +#ifndef SMALL_CONFIG + void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); +#else +# define GC_push_conditional(b, t, all) GC_push_all(b, t) +#endif + /* Do either of the above, depending */ + /* on the third arg. */ +void GC_push_all_stack(/*b,t*/); /* As above, but consider */ + /* interior pointers as valid */ +void GC_push_all_eager(/*b,t*/); /* Same as GC_push_all_stack, but */ + /* ensures that stack is scanned */ + /* immediately, not just scheduled */ + /* for scanning. */ +#ifndef THREADS + void GC_push_all_stack_partially_eager(/* bottom, top, cold_gc_frame */); + /* Similar to GC_push_all_eager, but only the */ + /* part hotter than cold_gc_frame is scanned */ + /* immediately. Needed to endure that callee- */ + /* save registers are not missed. */ +#else + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ +#endif +void GC_push_current_stack(/* ptr_t cold_gc_frame */); + /* Push enough of the current stack eagerly to */ + /* ensure that callee-save registers saved in */ + /* GC frames are scanned. */ + /* In the non-threads case, schedule entire */ + /* stack for scanning. */ +void GC_push_roots(/* GC_bool all, ptr_t cold_gc_frame */); + /* Push all or dirty roots. */ +extern void (*GC_push_other_roots)(); + /* Push system or application specific roots */ + /* onto the mark stack. In some environments */ + /* (e.g. threads environments) this is */ + /* predfined to be non-zero. A client supplied */ + /* replacement should also call the original */ + /* function. */ +extern void (*GC_start_call_back)(/* void */); + /* Called at start of full collections. */ + /* Not called if 0. Called with allocation */ + /* lock held. */ + /* 0 by default. */ +void GC_push_regs(); /* Push register contents onto mark stack. */ +void GC_remark(); /* Mark from all marked objects. Used */ + /* only if we had to drop something. */ +# if defined(MSWIN32) + void __cdecl GC_push_one(); +# else + void GC_push_one(/*p*/); /* If p points to an object, mark it */ + /* and push contents on the mark stack */ +# endif +void GC_push_one_checked(/*p*/); /* Ditto, omits plausibility test */ +void GC_push_marked(/* struct hblk h, hdr * hhdr */); + /* Push contents of all marked objects in h onto */ + /* mark stack. */ +#ifdef SMALL_CONFIG +# define GC_push_next_marked_dirty(h) GC_push_next_marked(h) +#else + struct hblk * GC_push_next_marked_dirty(/* h */); + /* Invoke GC_push_marked on next dirty block above h. */ + /* Return a pointer just past the end of this block. */ +#endif /* !SMALL_CONFIG */ +struct hblk * GC_push_next_marked(/* h */); + /* Ditto, but also mark from clean pages. */ +struct hblk * GC_push_next_marked_uncollectable(/* h */); + /* Ditto, but mark only from uncollectable pages. */ +GC_bool GC_stopped_mark(); /* Stop world and mark from all roots */ + /* and rescuers. */ +void GC_clear_hdr_marks(/* hhdr */); /* Clear the mark bits in a header */ +void GC_set_hdr_marks(/* hhdr */); /* Set the mark bits in a header */ +void GC_add_roots_inner(); +GC_bool GC_is_static_root(/* ptr_t p */); + /* Is the address p in one of the registered static */ + /* root sections? */ +void GC_register_dynamic_libraries(); + /* Add dynamic library data sections to the root set. */ + +/* Machine dependent startup routines */ +ptr_t GC_get_stack_base(); +void GC_register_data_segments(); + +/* Black listing: */ +void GC_bl_init(); +# ifndef ALL_INTERIOR_POINTERS + void GC_add_to_black_list_normal(/* bits, maybe source */); + /* Register bits as a possible future false */ + /* reference from the heap or static data */ +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_normal(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_normal(bits) +# endif +# else +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits) +# endif +# endif + +void GC_add_to_black_list_stack(/* bits, maybe source */); +struct hblk * GC_is_black_listed(/* h, len */); + /* If there are likely to be false references */ + /* to a block starting at h of the indicated */ + /* length, then return the next plausible */ + /* starting location for h that might avoid */ + /* these false references. */ +void GC_promote_black_lists(); + /* Declare an end to a black listing phase. */ +void GC_unpromote_black_lists(); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ +word GC_number_stack_black_listed(/*struct hblk *start, struct hblk *endp1 */); + /* Return the number of (stack) blacklisted */ + /* blocks in the range for statistical */ + /* purposes. */ + +ptr_t GC_scratch_alloc(/*bytes*/); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. */ + +/* Heap block layout maps: */ +void GC_invalidate_map(/* hdr */); + /* Remove the object map associated */ + /* with the block. This identifies */ + /* the block as invalid to the mark */ + /* routines. */ +GC_bool GC_add_map_entry(/*sz*/); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +void GC_register_displacement_inner(/*offset*/); + /* Version of GC_register_displacement */ + /* that assumes lock is already held */ + /* and signals are already disabled. */ + +/* hblk allocation: */ +void GC_new_hblk(/*size_in_words, kind*/); + /* Allocate a new heap block, and build */ + /* a free list in it. */ +struct hblk * GC_allochblk(/*size_in_words, kind*/); + /* Allocate a heap block, clear it if */ + /* for composite objects, inform */ + /* the marker that block is valid */ + /* for objects of indicated size. */ + /* sz < 0 ==> atomic. */ +void GC_freehblk(); /* Deallocate a heap block and mark it */ + /* as invalid. */ + +/* Misc GC: */ +void GC_init_inner(); +GC_bool GC_expand_hp_inner(); +void GC_start_reclaim(/*abort_if_found*/); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +void GC_continue_reclaim(/*size, kind*/); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corr. free list is */ + /* empty. */ +void GC_reclaim_or_delete_all(); + /* Arrange for all reclaim lists to be */ + /* empty. Judiciously choose between */ + /* sweeping and discarding each page. */ +GC_bool GC_reclaim_all(/* GC_stop_func f*/); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if f returns TRUE. */ +GC_bool GC_block_empty(/* hhdr */); /* Block completely unmarked? */ +GC_bool GC_never_stop_func(); /* Returns FALSE. */ +GC_bool GC_try_to_collect_inner(/* GC_stop_func f */); + /* Collect; caller must have acquired */ + /* lock and disabled signals. */ + /* Collection is aborted if f returns */ + /* TRUE. Returns TRUE if it completes */ + /* successfully. */ +# define GC_gcollect_inner() \ + (void) GC_try_to_collect_inner(GC_never_stop_func) +void GC_finish_collection(); /* Finish collection. Mark bits are */ + /* consistent and lock is still held. */ +GC_bool GC_collect_or_expand(/* needed_blocks */); + /* Collect or expand heap in an attempt */ + /* make the indicated number of free */ + /* blocks available. Should be called */ + /* until the blocks are available or */ + /* until it fails by returning FALSE. */ +GC_API void GC_init(); /* Initialize collector. */ +void GC_collect_a_little_inner(/* int n */); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ +ptr_t GC_generic_malloc(/* bytes, kind */); + /* Allocate an object of the given */ + /* kind. By default, there are only */ + /* a few kinds: composite(pointerfree), */ + /* atomic, uncollectable, etc. */ + /* We claim it's possible for clever */ + /* client code that understands GC */ + /* internals to add more, e.g. to */ + /* communicate object layout info */ + /* to the collector. */ +ptr_t GC_generic_malloc_ignore_off_page(/* bytes, kind */); + /* As above, but pointers past the */ + /* first page of the resulting object */ + /* are ignored. */ +ptr_t GC_generic_malloc_inner(/* bytes, kind */); + /* Ditto, but I already hold lock, etc. */ +ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind)); + /* As above, but size in units of words */ + /* Bypasses MERGE_SIZES. Assumes */ + /* words <= MAXOBJSZ. */ +ptr_t GC_generic_malloc_inner_ignore_off_page(/* bytes, kind */); + /* Allocate an object, where */ + /* the client guarantees that there */ + /* will always be a pointer to the */ + /* beginning of the object while the */ + /* object is live. */ +ptr_t GC_allocobj(/* sz_inn_words, kind */); + /* Make the indicated */ + /* free list nonempty, and return its */ + /* head. */ + +void GC_init_headers(); +GC_bool GC_install_header(/*h*/); + /* Install a header for block h. */ + /* Return FALSE on failure. */ +GC_bool GC_install_counts(/*h, sz*/); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +void GC_remove_header(/*h*/); + /* Remove the header for block h. */ +void GC_remove_counts(/*h, sz*/); + /* Remove forwarding counts for h. */ +hdr * GC_find_header(/*p*/); /* Debugging only. */ + +void GC_finalize(); /* Perform all indicated finalization actions */ + /* on unmarked objects. */ + /* Unreachable finalizable objects are enqueued */ + /* for processing by GC_invoke_finalizers. */ + /* Invoked with lock. */ + +void GC_add_to_heap(/*p, bytes*/); + /* Add a HBLKSIZE aligned chunk to the heap. */ + +void GC_print_obj(/* ptr_t p */); + /* P points to somewhere inside an object with */ + /* debugging info. Print a human readable */ + /* description of the object to stderr. */ +extern void (*GC_check_heap)(); + /* Check that all objects in the heap with */ + /* debugging info are intact. Print */ + /* descriptions of any that are not. */ +extern void (*GC_print_heap_obj)(/* ptr_t p */); + /* If possible print s followed by a more */ + /* detailed description of the object */ + /* referred to by p. */ + +/* Memory unmapping: */ +#ifdef USE_MUNMAP + void GC_unmap_old(void); + void GC_merge_unmapped(void); + void GC_unmap(ptr_t start, word bytes); + void GC_remap(ptr_t start, word bytes); + void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2); +#endif + +/* Virtual dirty bit implementation: */ +/* Each implementation exports the following: */ +void GC_read_dirty(); /* Retrieve dirty bits. */ +GC_bool GC_page_was_dirty(/* struct hblk * h */); + /* Read retrieved dirty bits. */ +GC_bool GC_page_was_ever_dirty(/* struct hblk * h */); + /* Could the page contain valid heap pointers? */ +void GC_is_fresh(/* struct hblk * h, word number_of_blocks */); + /* Assert the region currently contains no */ + /* valid pointers. */ +void GC_write_hint(/* struct hblk * h */); + /* h is about to be written. */ +void GC_dirty_init(); + +/* Slow/general mark bit manipulation: */ +GC_API GC_bool GC_is_marked(); +void GC_clear_mark_bit(); +void GC_set_mark_bit(); + +/* Stubborn objects: */ +void GC_read_changed(); /* Analogous to GC_read_dirty */ +GC_bool GC_page_was_changed(/* h */); /* Analogous to GC_page_was_dirty */ +void GC_clean_changing_list(); /* Collect obsolete changing list entries */ +void GC_stubborn_init(); + +/* Debugging print routines: */ +void GC_print_block_list(); +void GC_print_hblkfreelist(); +void GC_print_heap_sects(); +void GC_print_static_roots(); +void GC_dump(); + +#ifdef KEEP_BACK_PTRS + void GC_store_back_pointer(ptr_t source, ptr_t dest); + void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif + +/* Make arguments appear live to compiler */ +# ifdef __WATCOMC__ + void GC_noop(void*, ...); +# else + GC_API void GC_noop(); +# endif + +void GC_noop1(/* word arg */); + +/* Logging and diagnostic output: */ +GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long)); + /* A version of printf that doesn't allocate, */ + /* is restricted to long arguments, and */ + /* (unfortunately) doesn't use varargs for */ + /* portability. Restricted to 6 args and */ + /* 1K total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* allocate for long arguments.) */ +# define GC_printf0(f) GC_printf(f, 0l, 0l, 0l, 0l, 0l, 0l) +# define GC_printf1(f,a) GC_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) +# define GC_printf2(f,a,b) GC_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) +# define GC_printf3(f,a,b,c) GC_printf(f, (long)a, (long)b, (long)c, 0l, 0l, 0l) +# define GC_printf4(f,a,b,c,d) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, 0l, 0l) +# define GC_printf5(f,a,b,c,d,e) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, (long)e, 0l) +# define GC_printf6(f,a,b,c,d,e,g) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, (long)e, (long)g) + +void GC_err_printf(/* format, a, b, c, d, e, f */); +# define GC_err_printf0(f) GC_err_puts(f) +# define GC_err_printf1(f,a) GC_err_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) +# define GC_err_printf2(f,a,b) GC_err_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) +# define GC_err_printf3(f,a,b,c) GC_err_printf(f, (long)a, (long)b, (long)c, \ + 0l, 0l, 0l) +# define GC_err_printf4(f,a,b,c,d) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, 0l, 0l) +# define GC_err_printf5(f,a,b,c,d,e) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, 0l) +# define GC_err_printf6(f,a,b,c,d,e,g) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, (long)g) + /* Ditto, writes to stderr. */ + +void GC_err_puts(/* char *s */); + /* Write s to stderr, don't buffer, don't add */ + /* newlines, don't ... */ + + +# ifdef GC_ASSERTIONS +# define GC_ASSERT(expr) if(!(expr)) {\ + GC_err_printf2("Assertion failure: %s:%ld\n", \ + __FILE__, (unsigned long)__LINE__); \ + ABORT("assertion failure"); } +# else +# define GC_ASSERT(expr) +# endif + +# endif /* GC_PRIVATE_H */ diff --git a/gc/gc_private.h b/gc/gc_private.h new file mode 100644 index 0000000..3dd7c85 --- /dev/null +++ b/gc/gc_private.h @@ -0,0 +1 @@ +# include "gc_priv.h" diff --git a/gc/gc_typed.h b/gc/gc_typed.h new file mode 100644 index 0000000..e4a6b94 --- /dev/null +++ b/gc/gc_typed.h @@ -0,0 +1,91 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright 1996 Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Some simple primitives for allocation with explicit type information. + * Facilities for dynamic type inference may be added later. + * Should be used only for extremely performance critical applications, + * or if conservative collector leakage is otherwise a problem (unlikely). + * Note that this is implemented completely separately from the rest + * of the collector, and is not linked in unless referenced. + * This does not currently support GC_DEBUG in any interesting way. + */ +/* Boehm, May 19, 1994 2:13 pm PDT */ + +#ifndef _GC_TYPED_H +# define _GC_TYPED_H +# ifndef _GC_H +# include "gc.h" +# endif + +typedef GC_word * GC_bitmap; + /* The least significant bit of the first word is one if */ + /* the first word in the object may be a pointer. */ + +# define GC_get_bit(bm, index) \ + (((bm)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define GC_set_bit(bm, index) \ + (bm)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) + +typedef GC_word GC_descr; + +GC_API GC_descr GC_make_descriptor GC_PROTO((GC_bitmap bm, size_t len)); + /* Return a type descriptor for the object whose layout */ + /* is described by the argument. */ + /* The least significant bit of the first word is one */ + /* if the first word in the object may be a pointer. */ + /* The second argument specifies the number of */ + /* meaningful bits in the bitmap. The actual object */ + /* may be larger (but not smaller). Any additional */ + /* words in the object are assumed not to contain */ + /* pointers. */ + /* Returns a conservative approximation in the */ + /* (unlikely) case of insufficient memory to build */ + /* the descriptor. Calls to GC_make_descriptor */ + /* may consume some amount of a finite resource. This */ + /* is intended to be called once per type, not once */ + /* per allocation. */ + +GC_API GC_PTR GC_malloc_explicitly_typed + GC_PROTO((size_t size_in_bytes, GC_descr d)); + /* Allocate an object whose layout is described by d. */ + /* The resulting object MAY NOT BE PASSED TO REALLOC. */ + +GC_API GC_PTR GC_malloc_explicitly_typed_ignore_off_page + GC_PROTO((size_t size_in_bytes, GC_descr d)); + +GC_API GC_PTR GC_calloc_explicitly_typed + GC_PROTO((size_t nelements, + size_t element_size_in_bytes, + GC_descr d)); + /* Allocate an array of nelements elements, each of the */ + /* given size, and with the given descriptor. */ + /* The elemnt size must be a multiple of the byte */ + /* alignment required for pointers. E.g. on a 32-bit */ + /* machine with 16-bit aligned pointers, size_in_bytes */ + /* must be a multiple of 2. */ + +#ifdef GC_DEBUG +# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes) +# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) GC_MALLOC(n*bytes) +#else +# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) \ + GC_malloc_explicitly_typed(bytes, d) +# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) \ + GC_calloc_explicitly_typed(n, bytes, d) +#endif /* !GC_DEBUG */ + + +#endif /* _GC_TYPED_H */ + diff --git a/gc/gcc_support.c b/gc/gcc_support.c new file mode 100644 index 0000000..e8a7b82 --- /dev/null +++ b/gc/gcc_support.c @@ -0,0 +1,516 @@ +/*************************************************************************** + +Interface between g++ and Boehm GC + + Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to copy this code for any purpose, + provided the above notices are retained on all copies. + + Last modified on Sun Jul 16 23:21:14 PDT 1995 by ellis + +This module provides runtime support for implementing the +Ellis/Detlefs GC proposal, "Safe, Efficient Garbage Collection for +C++", within g++, using its -fgc-keyword extension. It defines +versions of __builtin_new, __builtin_new_gc, __builtin_vec_new, +__builtin_vec_new_gc, __builtin_delete, and __builtin_vec_delete that +invoke the Bohem GC. It also implements the WeakPointer.h interface. + +This module assumes the following configuration options of the Boehm GC: + + -DALL_INTERIOR_POINTERS + -DDONT_ADD_BYTE_AT_END + +This module adds its own required padding to the end of objects to +support C/C++ "one-past-the-object" pointer semantics. + +****************************************************************************/ + +#include <stddef.h> +#include "gc.h" + +#if defined(__STDC__) +# define PROTO( args ) args +#else +# define PROTO( args ) () +# endif + +#define BITSPERBYTE 8 + /* What's the portable way to do this? */ + + +typedef void (*vfp) PROTO(( void )); +extern vfp __new_handler; +extern void __default_new_handler PROTO(( void )); + + +/* A destructor_proc is the compiler generated procedure representing a +C++ destructor. The "flag" argument is a hidden argument following some +compiler convention. */ + +typedef (*destructor_proc) PROTO(( void* this, int flag )); + + +/*************************************************************************** + +A BI_header is the header the compiler adds to the front of +new-allocated arrays of objects with destructors. The header is +padded out to a double, because that's what the compiler does to +ensure proper alignment of array elements on some architectures. + +int NUM_ARRAY_ELEMENTS (void* o) + returns the number of array elements for array object o. + +char* FIRST_ELEMENT_P (void* o) + returns the address of the first element of array object o. + +***************************************************************************/ + +typedef struct BI_header { + int nelts; + char padding [sizeof( double ) - sizeof( int )]; + /* Better way to do this? */ +} BI_header; + +#define NUM_ARRAY_ELEMENTS( o ) \ + (((BI_header*) o)->nelts) + +#define FIRST_ELEMENT_P( o ) \ + ((char*) o + sizeof( BI_header )) + + +/*************************************************************************** + +The __builtin_new routines add a descriptor word to the end of each +object. The descriptor serves two purposes. + +First, the descriptor acts as padding, implementing C/C++ pointer +semantics. C and C++ allow a valid array pointer to be incremented +one past the end of an object. The extra padding ensures that the +collector will recognize that such a pointer points to the object and +not the next object in memory. + +Second, the descriptor stores three extra pieces of information, +whether an object has a registered finalizer (destructor), whether it +may have any weak pointers referencing it, and for collectible arrays, +the element size of the array. The element size is required for the +array's finalizer to iterate through the elements of the array. (An +alternative design would have the compiler generate a finalizer +procedure for each different array type. But given the overhead of +finalization, there isn't any efficiency to be gained by that.) + +The descriptor must be added to non-collectible as well as collectible +objects, since the Ellis/Detlefs proposal allows "pointer to gc T" to +be assigned to a "pointer to T", which could then be deleted. Thus, +__builtin_delete must determine at runtime whether an object is +collectible, whether it has weak pointers referencing it, and whether +it may have a finalizer that needs unregistering. Though +GC_REGISTER_FINALIZER doesn't care if you ask it to unregister a +finalizer for an object that doesn't have one, it is a non-trivial +procedure that does a hash look-up, etc. The descriptor trades a +little extra space for a significant increase in time on the fast path +through delete. (A similar argument applies to +GC_UNREGISTER_DISAPPEARING_LINK). + +For non-array types, the space for the descriptor could be shrunk to a +single byte for storing the "has finalizer" flag. But this would save +space only on arrays of char (whose size is not a multiple of the word +size) and structs whose largest member is less than a word in size +(very infrequent). And it would require that programmers actually +remember to call "delete[]" instead of "delete" (which they should, +but there are probably lots of buggy programs out there). For the +moment, the space savings seems not worthwhile, especially considering +that the Boehm GC is already quite space competitive with other +malloc's. + + +Given a pointer o to the base of an object: + +Descriptor* DESCRIPTOR (void* o) + returns a pointer to the descriptor for o. + +The implementation of descriptors relies on the fact that the GC +implementation allocates objects in units of the machine's natural +word size (e.g. 32 bits on a SPARC, 64 bits on an Alpha). + +**************************************************************************/ + +typedef struct Descriptor { + unsigned has_weak_pointers: 1; + unsigned has_finalizer: 1; + unsigned element_size: BITSPERBYTE * sizeof( unsigned ) - 2; +} Descriptor; + +#define DESCRIPTOR( o ) \ + ((Descriptor*) ((char*)(o) + GC_size( o ) - sizeof( Descriptor ))) + + +/************************************************************************** + +Implementations of global operator new() and operator delete() + +***************************************************************************/ + + +void* __builtin_new( size ) + size_t size; + /* + For non-gc non-array types, the compiler generates calls to + __builtin_new, which allocates non-collected storage via + GC_MALLOC_UNCOLLECTABLE. This ensures that the non-collected + storage will be part of the collector's root set, required by the + Ellis/Detlefs semantics. */ +{ + vfp handler = __new_handler ? __new_handler : __default_new_handler; + + while (1) { + void* o = GC_MALLOC_UNCOLLECTABLE( size + sizeof( Descriptor ) ); + if (o != 0) return o; + (*handler) ();}} + + +void* __builtin_vec_new( size ) + size_t size; + /* + For non-gc array types, the compiler generates calls to + __builtin_vec_new. */ +{ + return __builtin_new( size );} + + +void* __builtin_new_gc( size ) + size_t size; + /* + For gc non-array types, the compiler generates calls to + __builtin_new_gc, which allocates collected storage via + GC_MALLOC. */ +{ + vfp handler = __new_handler ? __new_handler : __default_new_handler; + + while (1) { + void* o = GC_MALLOC( size + sizeof( Descriptor ) ); + if (o != 0) return o; + (*handler) ();}} + + +void* __builtin_new_gc_a( size ) + size_t size; + /* + For non-pointer-containing gc non-array types, the compiler + generates calls to __builtin_new_gc_a, which allocates collected + storage via GC_MALLOC_ATOMIC. */ +{ + vfp handler = __new_handler ? __new_handler : __default_new_handler; + + while (1) { + void* o = GC_MALLOC_ATOMIC( size + sizeof( Descriptor ) ); + if (o != 0) return o; + (*handler) ();}} + + +void* __builtin_vec_new_gc( size ) + size_t size; + /* + For gc array types, the compiler generates calls to + __builtin_vec_new_gc. */ +{ + return __builtin_new_gc( size );} + + +void* __builtin_vec_new_gc_a( size ) + size_t size; + /* + For non-pointer-containing gc array types, the compiler generates + calls to __builtin_vec_new_gc_a. */ +{ + return __builtin_new_gc_a( size );} + + +static void call_destructor( o, data ) + void* o; + void* data; + /* + call_destructor is the GC finalizer proc registered for non-array + gc objects with destructors. Its client data is the destructor + proc, which it calls with the magic integer 2, a special flag + obeying the compiler convention for destructors. */ +{ + ((destructor_proc) data)( o, 2 );} + + +void* __builtin_new_gc_dtor( o, d ) + void* o; + destructor_proc d; + /* + The compiler generates a call to __builtin_new_gc_dtor to register + the destructor "d" of a non-array gc object "o" as a GC finalizer. + The destructor is registered via + GC_REGISTER_FINALIZER_IGNORE_SELF, which causes the collector to + ignore pointers from the object to itself when determining when + the object can be finalized. This is necessary due to the self + pointers used in the internal representation of multiply-inherited + objects. */ +{ + Descriptor* desc = DESCRIPTOR( o ); + + GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_destructor, d, 0, 0 ); + desc->has_finalizer = 1;} + + +static void call_array_destructor( o, data ) + void* o; + void* data; + /* + call_array_destructor is the GC finalizer proc registered for gc + array objects whose elements have destructors. Its client data is + the destructor proc. It iterates through the elements of the + array in reverse order, calling the destructor on each. */ +{ + int num = NUM_ARRAY_ELEMENTS( o ); + Descriptor* desc = DESCRIPTOR( o ); + size_t size = desc->element_size; + char* first_p = FIRST_ELEMENT_P( o ); + char* p = first_p + (num - 1) * size; + + if (num > 0) { + while (1) { + ((destructor_proc) data)( p, 2 ); + if (p == first_p) break; + p -= size;}}} + + +void* __builtin_vec_new_gc_dtor( first_elem, d, element_size ) + void* first_elem; + destructor_proc d; + size_t element_size; + /* + The compiler generates a call to __builtin_vec_new_gc_dtor to + register the destructor "d" of a gc array object as a GC + finalizer. "first_elem" points to the first element of the array, + *not* the beginning of the object (this makes the generated call + to this function smaller). The elements of the array are of size + "element_size". The destructor is registered as in + _builtin_new_gc_dtor. */ +{ + void* o = (char*) first_elem - sizeof( BI_header ); + Descriptor* desc = DESCRIPTOR( o ); + + GC_REGISTER_FINALIZER_IGNORE_SELF( o, call_array_destructor, d, 0, 0 ); + desc->element_size = element_size; + desc->has_finalizer = 1;} + + +void __builtin_delete( o ) + void* o; + /* + The compiler generates calls to __builtin_delete for operator + delete(). The GC currently requires that any registered + finalizers be unregistered before explicitly freeing an object. + If the object has any weak pointers referencing it, we can't + actually free it now. */ +{ + if (o != 0) { + Descriptor* desc = DESCRIPTOR( o ); + if (desc->has_finalizer) GC_REGISTER_FINALIZER( o, 0, 0, 0, 0 ); + if (! desc->has_weak_pointers) GC_FREE( o );}} + + +void __builtin_vec_delete( o ) + void* o; + /* + The compiler generates calls to __builitn_vec_delete for operator + delete[](). */ +{ + __builtin_delete( o );} + + +/************************************************************************** + +Implementations of the template class WeakPointer from WeakPointer.h + +***************************************************************************/ + +typedef struct WeakPointer { + void* pointer; +} WeakPointer; + + +void* _WeakPointer_New( t ) + void* t; +{ + if (t == 0) { + return 0;} + else { + void* base = GC_base( t ); + WeakPointer* wp = + (WeakPointer*) GC_MALLOC_ATOMIC( sizeof( WeakPointer ) ); + Descriptor* desc = DESCRIPTOR( base ); + + wp->pointer = t; + desc->has_weak_pointers = 1; + GC_general_register_disappearing_link( &wp->pointer, base ); + return wp;}} + + +static void* PointerWithLock( wp ) + WeakPointer* wp; +{ + if (wp == 0 || wp->pointer == 0) { + return 0;} + else { + return (void*) wp->pointer;}} + + +void* _WeakPointer_Pointer( wp ) + WeakPointer* wp; +{ + return (void*) GC_call_with_alloc_lock( PointerWithLock, wp );} + + +typedef struct EqualClosure { + WeakPointer* wp1; + WeakPointer* wp2; +} EqualClosure; + + +static void* EqualWithLock( ec ) + EqualClosure* ec; +{ + if (ec->wp1 == 0 || ec->wp2 == 0) { + return (void*) (ec->wp1 == ec->wp2);} + else { + return (void*) (ec->wp1->pointer == ec->wp2->pointer);}} + + +int _WeakPointer_Equal( wp1, wp2 ) + WeakPointer* wp1; + WeakPointer* wp2; +{ + EqualClosure ec; + + ec.wp1 = wp1; + ec.wp2 = wp2; + return (int) GC_call_with_alloc_lock( EqualWithLock, &ec );} + + +int _WeakPointer_Hash( wp ) + WeakPointer* wp; +{ + return (int) _WeakPointer_Pointer( wp );} + + +/************************************************************************** + +Implementations of the template class CleanUp from WeakPointer.h + +***************************************************************************/ + +typedef struct Closure { + void (*c) PROTO(( void* d, void* t )); + ptrdiff_t t_offset; + void* d; +} Closure; + + +static void _CleanUp_CallClosure( obj, data ) + void* obj; + void* data; +{ + Closure* closure = (Closure*) data; + closure->c( closure->d, (char*) obj + closure->t_offset );} + + +void _CleanUp_Set( t, c, d ) + void* t; + void (*c) PROTO(( void* d, void* t )); + void* d; +{ + void* base = GC_base( t ); + Descriptor* desc = DESCRIPTOR( t ); + + if (c == 0) { + GC_REGISTER_FINALIZER_IGNORE_SELF( base, 0, 0, 0, 0 ); + desc->has_finalizer = 0;} + else { + Closure* closure = (Closure*) GC_MALLOC( sizeof( Closure ) ); + closure->c = c; + closure->t_offset = (char*) t - (char*) base; + closure->d = d; + GC_REGISTER_FINALIZER_IGNORE_SELF( base, _CleanUp_CallClosure, + closure, 0, 0 ); + desc->has_finalizer = 1;}} + + +void _CleanUp_Call( t ) + void* t; +{ + /* ? Aren't we supposed to deactivate weak pointers to t too? + Why? */ + void* base = GC_base( t ); + void* d; + GC_finalization_proc f; + + GC_REGISTER_FINALIZER( base, 0, 0, &f, &d ); + f( base, d );} + + +typedef struct QueueElem { + void* o; + GC_finalization_proc f; + void* d; + struct QueueElem* next; +} QueueElem; + + +void* _CleanUp_Queue_NewHead() +{ + return GC_MALLOC( sizeof( QueueElem ) );} + + +static void _CleanUp_Queue_Enqueue( obj, data ) + void* obj; + void* data; +{ + QueueElem* q = (QueueElem*) data; + QueueElem* head = q->next; + + q->o = obj; + q->next = head->next; + head->next = q;} + + +void _CleanUp_Queue_Set( h, t ) + void* h; + void* t; +{ + QueueElem* head = (QueueElem*) h; + void* base = GC_base( t ); + void* d; + GC_finalization_proc f; + QueueElem* q = (QueueElem*) GC_MALLOC( sizeof( QueueElem ) ); + + GC_REGISTER_FINALIZER( base, _CleanUp_Queue_Enqueue, q, &f, &d ); + q->f = f; + q->d = d; + q->next = head;} + + +int _CleanUp_Queue_Call( h ) + void* h; +{ + QueueElem* head = (QueueElem*) h; + QueueElem* q = head->next; + + if (q == 0) { + return 0;} + else { + head->next = q->next; + q->next = 0; + if (q->f != 0) q->f( q->o, q->d ); + return 1;}} + + + diff --git a/gc/gcconfig.h b/gc/gcconfig.h new file mode 100644 index 0000000..189b13d --- /dev/null +++ b/gc/gcconfig.h @@ -0,0 +1,1110 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef CONFIG_H + +# define CONFIG_H + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_private.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +/* First a unified test for Linux: */ +# if defined(linux) || defined(__linux__) +# define LINUX +# endif + +/* Determine the machine type: */ +# if defined(sun) && defined(mc68000) +# define M68K +# define SUNOS4 +# define mach_type_known +# endif +# if defined(hp9000s300) +# define M68K +# define HP +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(m68k) +# define M68K +# define OPENBSD +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(__sparc__) +# define SPARC +# define OPENBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(m68k) +# define M68K +# define NETBSD +# define mach_type_known +# endif +# if defined(vax) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(mips) || defined(__mips) +# define MIPS +# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) +# define ULTRIX +# else +# if !defined(LINUX) +# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) +# define IRIX5 /* or IRIX 6.X */ +# else +# define RISCOS /* or IRIX 4.X */ +# endif +# endif +# endif +# define mach_type_known +# endif +# if defined(sequent) && defined(i386) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if defined(sun) && defined(i386) +# define I386 +# define SUNOS5 +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) +# define RT +# define mach_type_known +# endif +# if defined(sun) && (defined(sparc) || defined(__sparc)) +# define SPARC + /* Test for SunOS 5.x */ +# include <errno.h> +# ifdef ECHRNG +# define SUNOS5 +# else +# define SUNOS4 +# endif +# define mach_type_known +# endif +# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(__OpenBSD__) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# define RS6000 +# define mach_type_known +# endif +# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) + /* The above test may need refinement */ +# define I386 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) +# define M68K +# define SYSV +# define mach_type_known +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# define mach_type_known +# endif +# if defined(LINUX) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(LINUX) && defined(powerpc) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(LINUX) && defined(sparc) +# define SPARC +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(LINUX) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# if defined(THINK_C) || defined(__MWERKS__) && !defined(__powerc) +# define M68K +# define MACOS +# define mach_type_known +# endif +# if defined(__MWERKS__) && defined(__powerc) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(macosx) +# define MACOSX +# define POWERPC +# define mach_type_known +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && defined(i386) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(i386) +# define I386 +# define OPENBSD +# define mach_type_known +# endif +# if defined(__FreeBSD__) && defined(i386) +# define I386 +# define FREEBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(i386) +# define I386 +# define NETBSD +# define mach_type_known +# endif +# if defined(bsdi) && defined(i386) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if !defined(mach_type_known) && defined(__386BSD__) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define I386 +# define MSWIN32 /* or Win32s */ +# define mach_type_known +# endif +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# define I386 +# define CYGWIN32 +# define mach_type_known +# endif +# if defined(__BORLANDC__) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +/* Ivan Demakov */ +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif + +/* Feel free to add more clauses here */ + +/* Or manually define the machine type here. A machine type is */ +/* characterized by the architecture. Some */ +/* machine types are further subdivided by OS. */ +/* the macros ULTRIX, RISCOS, and BSD to distinguish. */ +/* Note that SGI IRIX is treated identically to RISCOS. */ +/* SYSV on an M68K actually means A/UX. */ +/* The distinction in these cases is usually the stack starting address */ +# ifndef mach_type_known + --> unknown machine type +# endif + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (SUNOS4,HP,NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI,SUNOS5, NEXT, other variants) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 or R3000 */ + /* (RISCOS, ULTRIX variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* RS6000 ==> IBM RS/6000 AIX3.X */ + /* RT ==> IBM PC/RT */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX */ + /* SPARC ==> SPARC under SunOS */ + /* (SUNOS4, SUNOS5, */ + /* DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORD_SZ is a simple integer constant representing the word size. + * in bits. We assume byte addressibility, where a byte has 8 bits. + * We also assume CPP_WORD_SZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64 bit processor with a C compiler that uses + * 32 bit pointers should use CPP_WORD_SZ of 32, not 64. Default is 32.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On UNIX systems, the collector will scan the area between DATASTART + * and DATAEND for root pointers. + * + * DATAEND, if not &end. + * + * ALIGN_DOUBLE of GC_malloc should return blocks aligned to twice + * the pointer size. + * + * STACKBOTTOM is the cool end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * HEURISTIC1 + * HEURISTIC2 + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signalled. Round that + * to the nearest plausible page boundary, and use that instead + * of STACKBOTTOM. + * + * If no expression for STACKBOTTOM can be found, and neither of the above + * heuristics are usable, the collector can still be used with all of the above + * undefined, provided one of the following is done: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like: + * + * # include "gc_private.h" + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return(real_main(argc, argv, envp)); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * MPROTECT_VDB: Write protect the heap and catch faults. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * + * An architecture may define DYNAMIC_LOADING if dynamic_load.c + * defined GC_register_dynamic_libraries() for the architecture. + */ + + +# define STACK_GRAN 0x1000000 +# ifdef M68K +# define MACH_TYPE "M68K" +# define ALIGNMENT 2 +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t)0xf0000000) +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + extern char etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x1ffff) & ~0x1ffff)) +# define HEURISTIC1 /* differs */ +# define DYNAMIC_LOADING +# endif +# ifdef HP +# define OS_TYPE "HP" + extern char etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t) 0xffeffffc) + /* empirically determined. seems to work. */ +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) +# endif +# ifdef SYSV +# define OS_TYPE "SYSV" + extern etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)&etext & 0x1fff)) + /* This only works for shared-text binaries with magic number 0413. + The other sorts of SysV binaries put the data at the end of the text, + in which case the default of &etext would work. Unfortunately, + handling both would require having the magic-number available. + -- Parag + */ +# define STACKBOTTOM ((ptr_t)0xFFFFFFFE) + /* The stack starts at the top of memory, but */ + /* 0x0 cannot be used as setjump_test complains */ + /* that the stack direction is incorrect. Two */ + /* bytes down from 0x0 should be safe enough. */ + /* --Parag */ +# include <sys/mmu.h> +# define GETPAGESIZE() PAGESIZE /* Is this still right? */ +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t) 0x4000000) +# define DATAEND /* not needed */ +# endif +# endif + +# ifdef POWERPC +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# define ALIGNMENT 4 /* Guess. Can someone verify? */ + /* This was 2, but that didn't sound right. */ +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 + /* Stack usually starts at 0x80000000 */ +# define DATASTART GC_data_start + extern int _end; +# define DATAEND (&_end) +# endif +# ifdef MACOSX +# define ALIGNMENT 4 +# define OS_TYPE "MACOSX" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t) 0xc0000000) +# define DATAEND /* not needed */ +# endif +# endif + +# ifdef VAX +# define MACH_TYPE "VAX" +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t) 0x7fffc800) +# endif +# endif + +# ifdef RT +# define MACH_TYPE "RT" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t) 0x10000000) +# define STACKBOTTOM ((ptr_t) 0x1fffd800) +# endif + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# define ALIGNMENT 4 /* Required by hardware */ +# define ALIGN_DOUBLE + extern int etext; +# ifdef SUNOS5 +# define OS_TYPE "SUNOS5" + extern int _etext; + extern int _end; + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +# define DATAEND (&_end) +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# define PROC_VDB +/* HEURISTIC1 reportedly no longer works under 2.7. Thus we */ +/* switched to HEURISTIC2, eventhough it creates some debugging */ +/* issues. */ +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least one */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + /* [If you have a weak stomach, don't read this.] */ + /* We would like to use: */ +/* # define DATASTART ((ptr_t)((((word) (&etext)) + 0x1fff) & ~0x1fff)) */ + /* This fails occasionally, due to an ancient, but very */ + /* persistent ld bug. &etext is set 32 bytes too high. */ + /* We instead read the text segment size from the a.out */ + /* header, which happens to be mapped into our address space */ + /* at the start of the text segment. The detective work here */ + /* was done by Robert Ehrlich, Manuel Serrano, and Bernard */ + /* Serpette of INRIA. */ + /* This assumes ZMAGIC, i.e. demand-loadable executables. */ +# define TEXTSTART 0x2000 +# define DATASTART ((ptr_t)(*(int *)(TEXTSTART+0x4)+TEXTSTART)) +# define MPROTECT_VDB +# define HEURISTIC1 +# define DYNAMIC_LOADING +# endif +# ifdef DRSNX +# define CPP_WORDSZ 32 +# define OS_TYPE "DRSNX" + extern char * GC_SysVGetDataStart(); + extern int etext; +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t) 0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + Linux Sparc non elf ? +# endif + extern int _end; +# define DATAEND (&_end) +# define SVR4 +# define STACKBOTTOM ((ptr_t) 0xf0000000) +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define STACKBOTTOM ((ptr_t) 0xf8000000) +# define DATASTART ((ptr_t)(&etext)) +# endif +# endif + +# ifdef I386 +# define MACH_TYPE "I386" +# define ALIGNMENT 4 /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. */ + /* Ivan Demakov: For Watcom the option is -zp4. */ +# ifndef SMALL_CONFIG +# define ALIGN_DOUBLE /* Not strictly necessary, but may give speed */ + /* improvement on Pentiums. */ +# endif +# ifdef SEQUENT +# define OS_TYPE "SEQUENT" + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t) 0x3ffff000) +# endif +# ifdef SUNOS5 +# define OS_TYPE "SUNOS5" + extern int etext, _start; + extern char * GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, &etext) +# define STACKBOTTOM ((ptr_t)(&_start)) +/** At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/*# define PROC_VDB*/ +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)&etext & 0xfff)) +# define STACKBOTTOM ((ptr_t) 0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext; +# define DATASTART ((ptr_t)(&etext)) +# define STACKBOTTOM ((ptr_t) 0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 + /* STACKBOTTOM is usually 0xc0000000, but this changes with */ + /* different kernel configurations. In particular, systems */ + /* with 2GB physical memory will usually move the user */ + /* address space limit, and hence initial SP to 0x80000000. */ +# if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# ifdef __ELF__ +# define DYNAMIC_LOADING +# ifdef UNDEFINED /* includes ro data */ + extern int _etext; +# define DATASTART ((ptr_t)((((word) (&_etext)) + 0xfff) & ~0xfff)) +# endif +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" + extern int _data_start__; + extern int _data_end__; + extern int _bss_start__; + extern int _bss_end__; + /* For binutils 2.9.1, we have */ + /* DATASTART = _data_start__ */ + /* DATAEND = _bss_end__ */ + /* whereas for some earlier versions it was */ + /* DATASTART = _bss_start__ */ + /* DATAEND = _data_end__ */ + /* To get it right for both, we take the */ + /* minumum/maximum of the two. */ +# define MAX(x,y) ((x) > (y) ? (x) : (y)) +# define MIN(x,y) ((x) < (y) ? (x) : (y)) +# define DATASTART ((ptr_t) MIN(&_data_start__, &_bss_start__)) +# define DATAEND ((ptr_t) MAX(&_data_end__, &_bss_end__)) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. OS2 actually has the right */ + /* system call! */ +# define DATAEND /* not needed */ +# endif +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# ifndef __WATCOMC__ +# define MPROTECT_VDB +# endif +# define DATAEND /* not needed */ +# endif +# ifdef DJGPP +# define OS_TYPE "DJGPP" +# include "stubinfo.h" + extern int etext; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x1ff) & ~0x1ff)) +/* # define STACKBOTTOM ((ptr_t)((word) _stubinfo + _stubinfo->size \ + + _stklen)) */ +# define STACKBOTTOM ((ptr_t)((word) __djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define MPROTECT_VDB +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# endif +# if defined(OPENBSD) || defined(FREEBSD) || defined(NETBSD) \ + || defined(THREE86BSD) || defined(BSDI) +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define DATAEND /* not needed */ +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes + or does not precedes with undescore names of C-variables. + Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t) _STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t) &__nullarea) +# define DATAEND ((ptr_t) &_end) +# endif +# endif + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define ALIGNMENT 4 + extern char **environ; +# define DATASTART ((ptr_t)(&environ)) + /* hideous kludge: environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ +# define STACKBOTTOM ((ptr_t) 0xfffff000) /* for Encore */ +# endif + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifdef LINUX +# define OS_TYPE "LINUX" + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# define ALIGNMENT 4 +# define USE_GENERIC_PUSH_REGS 1 +# define STACKBOTTOM 0x80000000 +# else // LINUX +# ifndef IRIX5 +# define DATASTART (ptr_t)0x10000000 + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# else // IRIX5 + extern int _fdata; +# define DATASTART ((ptr_t)(&_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +# endif /* IRIX5 */ +# define HEURISTIC2 +/* # define STACKBOTTOM ((ptr_t)0x7fff8000) sometimes also works. */ +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define ALIGNMENT 4 +# endif +# ifdef RISCOS +# define OS_TYPE "RISCOS" +# define ALIGNMENT 4 /* Required by hardware */ +# endif +# ifdef IRIX5 +# define OS_TYPE "IRIX5" +# define MPROTECT_VDB +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# if CPP_WORDSZ != 64 +# define ALIGN_DOUBLE +# endif +# else +# define ALIGNMENT 4 +# define ALIGN_DOUBLE +# endif +# define DYNAMIC_LOADING +# endif +# endif // LINUX +# endif + +# ifdef RS6000 +# define MACH_TYPE "RS6000" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t)0x20000000) + extern int errno; +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# if 0 + /* The following appears to work for 7xx systems running HP/UX */ + /* 9.xx Furthermore, it might result in much faster */ + /* collections than HEURISTIC2, which may involve scanning */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ +# define STACKBOTTOM ((ptr_t) 0x7b033000) /* from /etc/conf/h/param.h */ +# else +# define HEURISTIC2 +# endif +# define STACK_GROWS_UP +# define DYNAMIC_LOADING +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) + /* They misspelled the Posix macro? */ +# endif + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define ALIGNMENT 8 +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t) 0x140000000) + extern _end; +# define DATAEND ((ptr_t) &_end) +# define HEURISTIC2 + /* Normally HEURISTIC2 is too conervative, since */ + /* the text segment immediately follows the stack. */ + /* Hence we give an upper pound. */ + extern int __start; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) +# define CPP_WORDSZ 64 +# define MPROTECT_VDB +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t) 0x120000000) +# ifdef __ELF__ +# if 0 + /* __data_start apparently disappeared in some recent releases. */ + extern int __data_start; +# define DATASTART &__data_start +# endif +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x140000000) +# endif + extern int _end; +# define DATAEND (&_end) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif +# endif + +# ifdef M88K +# define MACH_TYPE "M88K" +# define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int etext; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART ((((word)&etext + 0x3fffff) & ~0x3fffff) + 0x10000) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) +# endif +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ +# endif + +# ifdef S370 +# define MACH_TYPE "S370" +# define OS_TYPE "UTS4" +# define ALIGNMENT 4 /* Required by hardware */ + extern int etext; + extern int _etext; + extern int _end; + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +# define DATAEND (&_end) +# define HEURISTIC2 +# endif + +# ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +# endif + +# ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +# endif + +# ifndef OS_TYPE +# define OS_TYPE "" +# endif + +# ifndef DATAEND + extern int end; +# define DATAEND (&end) +# endif + +# if defined(SVR4) && !defined(GETPAGESIZE) +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# endif + +# ifndef GETPAGESIZE +# if defined(SUNOS5) || defined(IRIX5) +# include <unistd.h> +# endif +# define GETPAGESIZE() getpagesize() +# endif + +# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. Probably others also qualify. */ +# define SVR4 +# endif + +# if defined(SUNOS5) || defined(DRSNX) + /* OS has SUNOS5 style semi-undocumented interface to dynamic */ + /* loader. */ +# define SUNOS5DL + /* OS has SUNOS5 style signal handlers. */ +# define SUNOS5SIGS +# endif + +# if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 + -> bad word size +# endif + +# ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +# endif + +# ifdef SRC_M3 +/* Postponed for now. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef SMALL_CONFIG +/* Presumably not worth the space it takes. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef USE_MUNMAP +# undef MPROTECT_VDB /* Can't deal with address space holes. */ +# endif + +# if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) +# define DEFAULT_VDB +# endif + +# if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +# endif +# if defined(IRIX_THREADS) && !defined(IRIX5) +--> inconsistent configuration +# endif +# if defined(IRIX_JDK_THREADS) && !defined(IRIX5) +--> inconsistent configuration +# endif +# if defined(LINUX_THREADS) && !defined(LINUX) +--> inconsistent configuration +# endif +# if defined(SOLARIS_THREADS) && !defined(SUNOS5) +--> inconsistent configuration +# endif +# if defined(PCR) || defined(SRC_M3) || \ + defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) +# define THREADS +# endif + +# if defined(HP_PA) || defined(M88K) || defined(POWERPC) \ + || (defined(I386) && defined(OS2)) || defined(UTS4) || defined(LINT) + /* Use setjmp based hack to mark from callee-save registers. */ +# define USE_GENERIC_PUSH_REGS +# endif +# if defined(SPARC) && !defined(LINUX) +# define SAVE_CALL_CHAIN +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +# endif + +# endif diff --git a/gc/headers.c b/gc/headers.c new file mode 100644 index 0000000..9564a6a --- /dev/null +++ b/gc/headers.c @@ -0,0 +1,351 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * This implements: + * 1. allocation of heap block headers + * 2. A map from addresses to heap block addresses to heap block headers + * + * Access speed is crucial. We implement an index structure based on a 2 + * level tree. + */ + +# include "gc_priv.h" + +bottom_index * GC_all_bottom_indices = 0; + /* Pointer to first (lowest addr) */ + /* bottom_index. */ + +bottom_index * GC_all_bottom_indices_end = 0; + /* Pointer to last (highest addr) */ + /* bottom_index. */ + +/* Non-macro version of header location routine */ +hdr * GC_find_header(h) +ptr_t h; +{ +# ifdef HASH_TL + register hdr * result; + GET_HDR(h, result); + return(result); +# else + return(HDR_INNER(h)); +# endif +} + +/* Routines to dynamically allocate collector data structures that will */ +/* never be freed. */ + +static ptr_t scratch_free_ptr = 0; + +ptr_t GC_scratch_end_ptr = 0; + +ptr_t GC_scratch_last_end_ptr = 0; + /* End point of last obtained scratch area */ + +ptr_t GC_scratch_alloc(bytes) +register word bytes; +{ + register ptr_t result = scratch_free_ptr; + +# ifdef ALIGN_DOUBLE +# define GRANULARITY (2 * sizeof(word)) +# else +# define GRANULARITY sizeof(word) +# endif + bytes += GRANULARITY-1; + bytes &= ~(GRANULARITY-1); + scratch_free_ptr += bytes; + if (scratch_free_ptr <= GC_scratch_end_ptr) { + return(result); + } + { + word bytes_to_get = MINHINCR * HBLKSIZE; + + if (bytes_to_get <= bytes) { + /* Undo the damage, and get memory directly */ + bytes_to_get = bytes; +# ifdef USE_MMAP + bytes_to_get += GC_page_size - 1; + bytes_to_get &= ~(GC_page_size - 1); +# endif + result = (ptr_t)GET_MEM(bytes_to_get); + scratch_free_ptr -= bytes; + GC_scratch_last_end_ptr = result + bytes; + return(result); + } + result = (ptr_t)GET_MEM(bytes_to_get); + if (result == 0) { +# ifdef PRINTSTATS + GC_printf0("Out of memory - trying to allocate less\n"); +# endif + scratch_free_ptr -= bytes; + bytes_to_get = bytes; +# ifdef USE_MMAP + bytes_to_get += GC_page_size - 1; + bytes_to_get &= ~(GC_page_size - 1); +# endif + return((ptr_t)GET_MEM(bytes_to_get)); + } + scratch_free_ptr = result; + GC_scratch_end_ptr = scratch_free_ptr + bytes_to_get; + GC_scratch_last_end_ptr = GC_scratch_end_ptr; + return(GC_scratch_alloc(bytes)); + } +} + +static hdr * hdr_free_list = 0; + +/* Return an uninitialized header */ +static hdr * alloc_hdr() +{ + register hdr * result; + + if (hdr_free_list == 0) { + result = (hdr *) GC_scratch_alloc((word)(sizeof(hdr))); + } else { + result = hdr_free_list; + hdr_free_list = (hdr *) (result -> hb_next); + } + return(result); +} + +static void free_hdr(hhdr) +hdr * hhdr; +{ + hhdr -> hb_next = (struct hblk *) hdr_free_list; + hdr_free_list = hhdr; +} + +void GC_init_headers() +{ + register unsigned i; + + GC_all_nils = (bottom_index *)GC_scratch_alloc((word)sizeof(bottom_index)); + BZERO(GC_all_nils, sizeof(bottom_index)); + for (i = 0; i < TOP_SZ; i++) { + GC_top_index[i] = GC_all_nils; + } +} + +/* Make sure that there is a bottom level index block for address addr */ +/* Return FALSE on failure. */ +static GC_bool get_index(addr) +word addr; +{ + word hi = (word)(addr) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bottom_index * r; + bottom_index * p; + bottom_index ** prev; + bottom_index *pi; + +# ifdef HASH_TL + unsigned i = TL_HASH(hi); + bottom_index * old; + + old = p = GC_top_index[i]; + while(p != GC_all_nils) { + if (p -> key == hi) return(TRUE); + p = p -> hash_link; + } + r = (bottom_index*)GC_scratch_alloc((word)(sizeof (bottom_index))); + if (r == 0) return(FALSE); + BZERO(r, sizeof (bottom_index)); + r -> hash_link = old; + GC_top_index[i] = r; +# else + if (GC_top_index[hi] != GC_all_nils) return(TRUE); + r = (bottom_index*)GC_scratch_alloc((word)(sizeof (bottom_index))); + if (r == 0) return(FALSE); + GC_top_index[hi] = r; + BZERO(r, sizeof (bottom_index)); +# endif + r -> key = hi; + /* Add it to the list of bottom indices */ + prev = &GC_all_bottom_indices; /* pointer to p */ + pi = 0; /* bottom_index preceding p */ + while ((p = *prev) != 0 && p -> key < hi) { + pi = p; + prev = &(p -> asc_link); + } + r -> desc_link = pi; + if (0 == p) { + GC_all_bottom_indices_end = r; + } else { + p -> desc_link = r; + } + r -> asc_link = p; + *prev = r; + return(TRUE); +} + +/* Install a header for block h. */ +/* The header is uninitialized. */ +/* Returns FALSE on failure. */ +GC_bool GC_install_header(h) +register struct hblk * h; +{ + hdr * result; + + if (!get_index((word) h)) return(FALSE); + result = alloc_hdr(); + SET_HDR(h, result); +# ifdef USE_MUNMAP + result -> hb_last_reclaimed = GC_gc_no; +# endif + return(result != 0); +} + +/* Set up forwarding counts for block h of size sz */ +GC_bool GC_install_counts(h, sz) +register struct hblk * h; +register word sz; /* bytes */ +{ + register struct hblk * hbp; + register int i; + + for (hbp = h; (char *)hbp < (char *)h + sz; hbp += BOTTOM_SZ) { + if (!get_index((word) hbp)) return(FALSE); + } + if (!get_index((word)h + sz - 1)) return(FALSE); + for (hbp = h + 1; (char *)hbp < (char *)h + sz; hbp += 1) { + i = HBLK_PTR_DIFF(hbp, h); + SET_HDR(hbp, (hdr *)(i > MAX_JUMP? MAX_JUMP : i)); + } + return(TRUE); +} + +/* Remove the header for block h */ +void GC_remove_header(h) +register struct hblk * h; +{ + hdr ** ha; + + GET_HDR_ADDR(h, ha); + free_hdr(*ha); + *ha = 0; +} + +/* Remove forwarding counts for h */ +void GC_remove_counts(h, sz) +register struct hblk * h; +register word sz; /* bytes */ +{ + register struct hblk * hbp; + + for (hbp = h+1; (char *)hbp < (char *)h + sz; hbp += 1) { + SET_HDR(hbp, 0); + } +} + +/* Apply fn to all allocated blocks */ +/*VARARGS1*/ +void GC_apply_to_all_blocks(fn, client_data) +void (*fn)(/* struct hblk *h, word client_data */); +word client_data; +{ + register int j; + register bottom_index * index_p; + + for (index_p = GC_all_bottom_indices; index_p != 0; + index_p = index_p -> asc_link) { + for (j = BOTTOM_SZ-1; j >= 0;) { + if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])) { + if (index_p->index[j]->hb_map != GC_invalid_map) { + (*fn)(((struct hblk *) + (((index_p->key << LOG_BOTTOM_SZ) + (word)j) + << LOG_HBLKSIZE)), + client_data); + } + j--; + } else if (index_p->index[j] == 0) { + j--; + } else { + j -= (word)(index_p->index[j]); + } + } + } +} + +/* Get the next valid block whose address is at least h */ +/* Return 0 if there is none. */ +struct hblk * GC_next_used_block(h) +struct hblk * h; +{ + register bottom_index * bi; + register word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GET_BI(h, bi); + if (bi == GC_all_nils) { + register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bi = GC_all_bottom_indices; + while (bi != 0 && bi -> key < hi) bi = bi -> asc_link; + j = 0; + } + while(bi != 0) { + while (j < BOTTOM_SZ) { + hdr * hhdr = bi -> index[j]; + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j++; + } else { + if (hhdr->hb_map != GC_invalid_map) { + return((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } else { + j += divHBLKSZ(hhdr -> hb_sz); + } + } + } + j = 0; + bi = bi -> asc_link; + } + return(0); +} + +/* Get the last (highest address) block whose address is */ +/* at most h. Return 0 if there is none. */ +/* Unlike the above, this may return a free block. */ +struct hblk * GC_prev_block(h) +struct hblk * h; +{ + register bottom_index * bi; + register signed_word j = ((word)h >> LOG_HBLKSIZE) & (BOTTOM_SZ-1); + + GET_BI(h, bi); + if (bi == GC_all_nils) { + register word hi = (word)h >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); + bi = GC_all_bottom_indices_end; + while (bi != 0 && bi -> key > hi) bi = bi -> desc_link; + j = BOTTOM_SZ - 1; + } + while(bi != 0) { + while (j >= 0) { + hdr * hhdr = bi -> index[j]; + if (0 == hhdr) { + --j; + } else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + j -= (signed_word)hhdr; + } else { + return((struct hblk *) + (((bi -> key << LOG_BOTTOM_SZ) + j) + << LOG_HBLKSIZE)); + } + } + j = BOTTOM_SZ - 1; + bi = bi -> desc_link; + } + return(0); +} diff --git a/gc/if_mach.c b/gc/if_mach.c new file mode 100644 index 0000000..af01363 --- /dev/null +++ b/gc/if_mach.c @@ -0,0 +1,25 @@ +/* Conditionally execute a command based on machine and OS from gcconfig.h */ + +# include "gcconfig.h" +# include <stdio.h> + +int main(argc, argv, envp) +int argc; +char ** argv; +char ** envp; +{ + if (argc < 4) goto Usage; + if (strcmp(MACH_TYPE, argv[1]) != 0) return(0); + if (strcmp(OS_TYPE, "") != 0 && strcmp(argv[2], "") != 0 + && strcmp(OS_TYPE, argv[2]) != 0) return(0); + printf("^^^^Starting command^^^^\n"); + execvp(argv[3], argv+3); + perror("Couldn't execute"); + +Usage: + fprintf(stderr, "Usage: %s mach_type os_type command\n", argv[0]); + fprintf(stderr, "Currently mach_type = %s, os_type = %s\n", + MACH_TYPE, OS_TYPE); + return(1); +} + diff --git a/gc/if_not_there.c b/gc/if_not_there.c new file mode 100644 index 0000000..a93795f --- /dev/null +++ b/gc/if_not_there.c @@ -0,0 +1,26 @@ +/* Conditionally execute a command based if the file argv[1] doesn't exist */ +/* Except for execvp, we stick to ANSI C. */ +# include "gcconfig.h" +# include <stdio.h> + +int main(argc, argv, envp) +int argc; +char ** argv; +char ** envp; +{ + FILE * f; + if (argc < 3) goto Usage; + if ((f = fopen(argv[1], "rb")) != 0 + || (f = fopen(argv[1], "r")) != 0) { + fclose(f); + return(0); + } + printf("^^^^Starting command^^^^\n"); + execvp(argv[2], argv+2); + exit(1); + +Usage: + fprintf(stderr, "Usage: %s file_name command\n", argv[0]); + return(1); +} + diff --git a/gc/include/backptr.h b/gc/include/backptr.h new file mode 100644 index 0000000..d34224e --- /dev/null +++ b/gc/include/backptr.h @@ -0,0 +1,56 @@ +/* + * This is a simple API to implement pointer back tracing, i.e. + * to answer questions such as "who is pointing to this" or + * "why is this object being retained by the collector" + * + * This API assumes that we have an ANSI C compiler. + * + * Most of these calls yield useful information on only after + * a garbage collection. Usually the client will first force + * a full collection and then gather information, preferably + * before much intervening allocation. + * + * The implementation of the interface is only about 99.9999% + * correct. It is intended to be good enough for profiling, + * but is not intended to be used with production code. + * + * Results are likely to be much more useful if all allocation is + * accomplished through the debugging allocators. + * + * The implementation idea is due to A. Demers. + */ + +/* Store information about the object referencing dest in *base_p */ +/* and *offset_p. */ +/* If multiple objects or roots point to dest, the one reported */ +/* will be the last on used by the garbage collector to trace the */ +/* object. */ +/* source is root ==> *base_p = address, *offset_p = 0 */ +/* source is heap object ==> *base_p != 0, *offset_p = offset */ +/* Returns 1 on success, 0 if source couldn't be determined. */ +/* Dest can be any address within a heap object. */ +typedef enum { GC_UNREFERENCED, /* No refence info available. */ + GC_NO_SPACE, /* Dest not allocated with debug alloc */ + GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p */ + GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */ + GC_FINALIZER_REFD /* Finalizable and hence accessible. */ +} GC_ref_kind; + +GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p); + +/* Generate a random heap address. */ +/* The resulting address is in the heap, but */ +/* not necessarily inside a valid object. */ +void * GC_generate_random_heap_address(void); + +/* Generate a random address inside a valid marked heap object. */ +void * GC_generate_random_valid_address(void); + +/* Force a garbage collection and generate a backtrace from a */ +/* random heap address. */ +/* This uses the GC logging mechanism (GC_printf) to produce */ +/* output. It can often be called from a debugger. The */ +/* source in dbg_mlc.c also serves as a sample client. */ +void GC_generate_random_backtrace(void); + + diff --git a/gc/include/cord.h b/gc/include/cord.h new file mode 100644 index 0000000..584112f --- /dev/null +++ b/gc/include/cord.h @@ -0,0 +1,327 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * Author: Hans-J. Boehm (boehm@parc.xerox.com) + */ +/* Boehm, October 5, 1995 4:20 pm PDT */ + +/* + * Cords are immutable character strings. A number of operations + * on long cords are much more efficient than their strings.h counterpart. + * In particular, concatenation takes constant time independent of the length + * of the arguments. (Cords are represented as trees, with internal + * nodes representing concatenation and leaves consisting of either C + * strings or a functional description of the string.) + * + * The following are reasonable applications of cords. They would perform + * unacceptably if C strings were used: + * - A compiler that produces assembly language output by repeatedly + * concatenating instructions onto a cord representing the output file. + * - A text editor that converts the input file to a cord, and then + * performs editing operations by producing a new cord representing + * the file after echa character change (and keeping the old ones in an + * edit history) + * + * For optimal performance, cords should be built by + * concatenating short sections. + * This interface is designed for maximum compatibility with C strings. + * ASCII NUL characters may be embedded in cords using CORD_from_fn. + * This is handled correctly, but CORD_to_char_star will produce a string + * with embedded NULs when given such a cord. + * + * This interface is fairly big, largely for performance reasons. + * The most basic constants and functions: + * + * CORD - the type fo a cord; + * CORD_EMPTY - empty cord; + * CORD_len(cord) - length of a cord; + * CORD_cat(cord1,cord2) - concatenation of two cords; + * CORD_substr(cord, start, len) - substring (or subcord); + * CORD_pos i; CORD_FOR(i, cord) { ... CORD_pos_fetch(i) ... } - + * examine each character in a cord. CORD_pos_fetch(i) is the char. + * CORD_fetch(int i) - Retrieve i'th character (slowly). + * CORD_cmp(cord1, cord2) - compare two cords. + * CORD_from_file(FILE * f) - turn a read-only file into a cord. + * CORD_to_char_star(cord) - convert to C string. + * (Non-NULL C constant strings are cords.) + * CORD_printf (etc.) - cord version of printf. Use %r for cords. + */ +# ifndef CORD_H + +# define CORD_H +# include <stddef.h> +# include <stdio.h> +/* Cords have type const char *. This is cheating quite a bit, and not */ +/* 100% portable. But it means that nonempty character string */ +/* constants may be used as cords directly, provided the string is */ +/* never modified in place. The empty cord is represented by, and */ +/* can be written as, 0. */ + +typedef const char * CORD; + +/* An empty cord is always represented as nil */ +# define CORD_EMPTY 0 + +/* Is a nonempty cord represented as a C string? */ +#define CORD_IS_STRING(s) (*(s) != '\0') + +/* Concatenate two cords. If the arguments are C strings, they may */ +/* not be subsequently altered. */ +CORD CORD_cat(CORD x, CORD y); + +/* Concatenate a cord and a C string with known length. Except for the */ +/* empty string case, this is a special case of CORD_cat. Since the */ +/* length is known, it can be faster. */ +/* The string y is shared with the resulting CORD. Hence it should */ +/* not be altered by the caller. */ +CORD CORD_cat_char_star(CORD x, const char * y, size_t leny); + +/* Compute the length of a cord */ +size_t CORD_len(CORD x); + +/* Cords may be represented by functions defining the ith character */ +typedef char (* CORD_fn)(size_t i, void * client_data); + +/* Turn a functional description into a cord. */ +CORD CORD_from_fn(CORD_fn fn, void * client_data, size_t len); + +/* Return the substring (subcord really) of x with length at most n, */ +/* starting at position i. (The initial character has position 0.) */ +CORD CORD_substr(CORD x, size_t i, size_t n); + +/* Return the argument, but rebalanced to allow more efficient */ +/* character retrieval, substring operations, and comparisons. */ +/* This is useful only for cords that were built using repeated */ +/* concatenation. Guarantees log time access to the result, unless */ +/* x was obtained through a large number of repeated substring ops */ +/* or the embedded functional descriptions take longer to evaluate. */ +/* May reallocate significant parts of the cord. The argument is not */ +/* modified; only the result is balanced. */ +CORD CORD_balance(CORD x); + +/* The following traverse a cord by applying a function to each */ +/* character. This is occasionally appropriate, especially where */ +/* speed is crucial. But, since C doesn't have nested functions, */ +/* clients of this sort of traversal are clumsy to write. Consider */ +/* the functions that operate on cord positions instead. */ + +/* Function to iteratively apply to individual characters in cord. */ +typedef int (* CORD_iter_fn)(char c, void * client_data); + +/* Function to apply to substrings of a cord. Each substring is a */ +/* a C character string, not a general cord. */ +typedef int (* CORD_batched_iter_fn)(const char * s, void * client_data); +# define CORD_NO_FN ((CORD_batched_iter_fn)0) + +/* Apply f1 to each character in the cord, in ascending order, */ +/* starting at position i. If */ +/* f2 is not CORD_NO_FN, then multiple calls to f1 may be replaced by */ +/* a single call to f2. The parameter f2 is provided only to allow */ +/* some optimization by the client. This terminates when the right */ +/* end of this string is reached, or when f1 or f2 return != 0. In the */ +/* latter case CORD_iter returns != 0. Otherwise it returns 0. */ +/* The specified value of i must be < CORD_len(x). */ +int CORD_iter5(CORD x, size_t i, CORD_iter_fn f1, + CORD_batched_iter_fn f2, void * client_data); + +/* A simpler version that starts at 0, and without f2: */ +int CORD_iter(CORD x, CORD_iter_fn f1, void * client_data); +# define CORD_iter(x, f1, cd) CORD_iter5(x, 0, f1, CORD_NO_FN, cd) + +/* Similar to CORD_iter5, but end-to-beginning. No provisions for */ +/* CORD_batched_iter_fn. */ +int CORD_riter4(CORD x, size_t i, CORD_iter_fn f1, void * client_data); + +/* A simpler version that starts at the end: */ +int CORD_riter(CORD x, CORD_iter_fn f1, void * client_data); + +/* Functions that operate on cord positions. The easy way to traverse */ +/* cords. A cord position is logically a pair consisting of a cord */ +/* and an index into that cord. But it is much faster to retrieve a */ +/* charcter based on a position than on an index. Unfortunately, */ +/* positions are big (order of a few 100 bytes), so allocate them with */ +/* caution. */ +/* Things in cord_pos.h should be treated as opaque, except as */ +/* described below. Also note that */ +/* CORD_pos_fetch, CORD_next and CORD_prev have both macro and function */ +/* definitions. The former may evaluate their argument more than once. */ +# include "private/cord_pos.h" + +/* + Visible definitions from above: + + typedef <OPAQUE but fairly big> CORD_pos[1]; + + * Extract the cord from a position: + CORD CORD_pos_to_cord(CORD_pos p); + + * Extract the current index from a position: + size_t CORD_pos_to_index(CORD_pos p); + + * Fetch the character located at the given position: + char CORD_pos_fetch(CORD_pos p); + + * Initialize the position to refer to the given cord and index. + * Note that this is the most expensive function on positions: + void CORD_set_pos(CORD_pos p, CORD x, size_t i); + + * Advance the position to the next character. + * P must be initialized and valid. + * Invalidates p if past end: + void CORD_next(CORD_pos p); + + * Move the position to the preceding character. + * P must be initialized and valid. + * Invalidates p if past beginning: + void CORD_prev(CORD_pos p); + + * Is the position valid, i.e. inside the cord? + int CORD_pos_valid(CORD_pos p); +*/ +# define CORD_FOR(pos, cord) \ + for (CORD_set_pos(pos, cord, 0); CORD_pos_valid(pos); CORD_next(pos)) + + +/* An out of memory handler to call. May be supplied by client. */ +/* Must not return. */ +extern void (* CORD_oom_fn)(void); + +/* Dump the representation of x to stdout in an implementation defined */ +/* manner. Intended for debugging only. */ +void CORD_dump(CORD x); + +/* The following could easily be implemented by the client. They are */ +/* provided in cordxtra.c for convenience. */ + +/* Concatenate a character to the end of a cord. */ +CORD CORD_cat_char(CORD x, char c); + +/* Concatenate n cords. */ +CORD CORD_catn(int n, /* CORD */ ...); + +/* Return the character in CORD_substr(x, i, 1) */ +char CORD_fetch(CORD x, size_t i); + +/* Return < 0, 0, or > 0, depending on whether x < y, x = y, x > y */ +int CORD_cmp(CORD x, CORD y); + +/* A generalization that takes both starting positions for the */ +/* comparison, and a limit on the number of characters to be compared. */ +int CORD_ncmp(CORD x, size_t x_start, CORD y, size_t y_start, size_t len); + +/* Find the first occurrence of s in x at position start or later. */ +/* Return the position of the first character of s in x, or */ +/* CORD_NOT_FOUND if there is none. */ +size_t CORD_str(CORD x, size_t start, CORD s); + +/* Return a cord consisting of i copies of (possibly NUL) c. Dangerous */ +/* in conjunction with CORD_to_char_star. */ +/* The resulting representation takes constant space, independent of i. */ +CORD CORD_chars(char c, size_t i); +# define CORD_nul(i) CORD_chars('\0', (i)) + +/* Turn a file into cord. The file must be seekable. Its contents */ +/* must remain constant. The file may be accessed as an immediate */ +/* result of this call and/or as a result of subsequent accesses to */ +/* the cord. Short files are likely to be immediately read, but */ +/* long files are likely to be read on demand, possibly relying on */ +/* stdio for buffering. */ +/* We must have exclusive access to the descriptor f, i.e. we may */ +/* read it at any time, and expect the file pointer to be */ +/* where we left it. Normally this should be invoked as */ +/* CORD_from_file(fopen(...)) */ +/* CORD_from_file arranges to close the file descriptor when it is no */ +/* longer needed (e.g. when the result becomes inaccessible). */ +/* The file f must be such that ftell reflects the actual character */ +/* position in the file, i.e. the number of characters that can be */ +/* or were read with fread. On UNIX systems this is always true. On */ +/* MS Windows systems, f must be opened in binary mode. */ +CORD CORD_from_file(FILE * f); + +/* Equivalent to the above, except that the entire file will be read */ +/* and the file pointer will be closed immediately. */ +/* The binary mode restriction from above does not apply. */ +CORD CORD_from_file_eager(FILE * f); + +/* Equivalent to the above, except that the file will be read on demand.*/ +/* The binary mode restriction applies. */ +CORD CORD_from_file_lazy(FILE * f); + +/* Turn a cord into a C string. The result shares no structure with */ +/* x, and is thus modifiable. */ +char * CORD_to_char_star(CORD x); + +/* Turn a C string into a CORD. The C string is copied, and so may */ +/* subsequently be modified. */ +CORD CORD_from_char_star(const char *s); + +/* Identical to the above, but the result may share structure with */ +/* the argument and is thus not modifiable. */ +const char * CORD_to_const_char_star(CORD x); + +/* Write a cord to a file, starting at the current position. No */ +/* trailing NULs are newlines are added. */ +/* Returns EOF if a write error occurs, 1 otherwise. */ +int CORD_put(CORD x, FILE * f); + +/* "Not found" result for the following two functions. */ +# define CORD_NOT_FOUND ((size_t)(-1)) + +/* A vague analog of strchr. Returns the position (an integer, not */ +/* a pointer) of the first occurrence of (char) c inside x at position */ +/* i or later. The value i must be < CORD_len(x). */ +size_t CORD_chr(CORD x, size_t i, int c); + +/* A vague analog of strrchr. Returns index of the last occurrence */ +/* of (char) c inside x at position i or earlier. The value i */ +/* must be < CORD_len(x). */ +size_t CORD_rchr(CORD x, size_t i, int c); + + +/* The following are also not primitive, but are implemented in */ +/* cordprnt.c. They provide functionality similar to the ANSI C */ +/* functions with corresponding names, but with the following */ +/* additions and changes: */ +/* 1. A %r conversion specification specifies a CORD argument. Field */ +/* width, precision, etc. have the same semantics as for %s. */ +/* (Note that %c,%C, and %S were already taken.) */ +/* 2. The format string is represented as a CORD. */ +/* 3. CORD_sprintf and CORD_vsprintf assign the result through the 1st */ /* argument. Unlike their ANSI C versions, there is no need to guess */ +/* the correct buffer size. */ +/* 4. Most of the conversions are implement through the native */ +/* vsprintf. Hence they are usually no faster, and */ +/* idiosyncracies of the native printf are preserved. However, */ +/* CORD arguments to CORD_sprintf and CORD_vsprintf are NOT copied; */ +/* the result shares the original structure. This may make them */ +/* very efficient in some unusual applications. */ +/* The format string is copied. */ +/* All functions return the number of characters generated or -1 on */ +/* error. This complies with the ANSI standard, but is inconsistent */ +/* with some older implementations of sprintf. */ + +/* The implementation of these is probably less portable than the rest */ +/* of this package. */ + +#ifndef CORD_NO_IO + +#include <stdarg.h> + +int CORD_sprintf(CORD * out, CORD format, ...); +int CORD_vsprintf(CORD * out, CORD format, va_list args); +int CORD_fprintf(FILE * f, CORD format, ...); +int CORD_vfprintf(FILE * f, CORD format, va_list args); +int CORD_printf(CORD format, ...); +int CORD_vprintf(CORD format, va_list args); + +#endif /* CORD_NO_IO */ + +# endif /* CORD_H */ diff --git a/gc/include/ec.h b/gc/include/ec.h new file mode 100644 index 0000000..c829b83 --- /dev/null +++ b/gc/include/ec.h @@ -0,0 +1,70 @@ +# ifndef EC_H +# define EC_H + +# ifndef CORD_H +# include "cord.h" +# endif + +/* Extensible cords are strings that may be destructively appended to. */ +/* They allow fast construction of cords from characters that are */ +/* being read from a stream. */ +/* + * A client might look like: + * + * { + * CORD_ec x; + * CORD result; + * char c; + * FILE *f; + * + * ... + * CORD_ec_init(x); + * while(...) { + * c = getc(f); + * ... + * CORD_ec_append(x, c); + * } + * result = CORD_balance(CORD_ec_to_cord(x)); + * + * If a C string is desired as the final result, the call to CORD_balance + * may be replaced by a call to CORD_to_char_star. + */ + +# ifndef CORD_BUFSZ +# define CORD_BUFSZ 128 +# endif + +typedef struct CORD_ec_struct { + CORD ec_cord; + char * ec_bufptr; + char ec_buf[CORD_BUFSZ+1]; +} CORD_ec[1]; + +/* This structure represents the concatenation of ec_cord with */ +/* ec_buf[0 ... (ec_bufptr-ec_buf-1)] */ + +/* Flush the buffer part of the extended chord into ec_cord. */ +/* Note that this is almost the only real function, and it is */ +/* implemented in 6 lines in cordxtra.c */ +void CORD_ec_flush_buf(CORD_ec x); + +/* Convert an extensible cord to a cord. */ +# define CORD_ec_to_cord(x) (CORD_ec_flush_buf(x), (x)[0].ec_cord) + +/* Initialize an extensible cord. */ +# define CORD_ec_init(x) ((x)[0].ec_cord = 0, (x)[0].ec_bufptr = (x)[0].ec_buf) + +/* Append a character to an extensible cord. */ +# define CORD_ec_append(x, c) \ + { \ + if ((x)[0].ec_bufptr == (x)[0].ec_buf + CORD_BUFSZ) { \ + CORD_ec_flush_buf(x); \ + } \ + *((x)[0].ec_bufptr)++ = (c); \ + } + +/* Append a cord to an extensible cord. Structure remains shared with */ +/* original. */ +void CORD_ec_append_cord(CORD_ec x, CORD s); + +# endif /* EC_H */ diff --git a/gc/include/gc.h b/gc/include/gc.h new file mode 100644 index 0000000..3061409 --- /dev/null +++ b/gc/include/gc.h @@ -0,0 +1,754 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * Note that this defines a large number of tuning hooks, which can + * safely be ignored in nearly all cases. For normal use it suffices + * to call only GC_MALLOC and perhaps GC_REALLOC. + * For better performance, also look at GC_MALLOC_ATOMIC, and + * GC_enable_incremental. If you need an action to be performed + * immediately before an object is collected, look at GC_register_finalizer. + * If you are using Solaris threads, look at the end of this file. + * Everything else is best ignored unless you encounter performance + * problems. + */ + +#ifndef _GC_H + +# define _GC_H +# define __GC +# include <stddef.h> + +#if defined(__CYGWIN32__) && defined(GC_USE_DLL) +#include "libgc_globals.h" +#endif + +#if defined(_MSC_VER) && defined(_DLL) +# ifdef GC_BUILD +# define GC_API __declspec(dllexport) +# else +# define GC_API __declspec(dllimport) +# endif +#endif + +#if defined(__WATCOMC__) && defined(GC_DLL) +# ifdef GC_BUILD +# define GC_API extern __declspec(dllexport) +# else +# define GC_API extern __declspec(dllimport) +# endif +#endif + +#ifndef GC_API +#define GC_API extern +#endif + +# if defined(__STDC__) || defined(__cplusplus) +# define GC_PROTO(args) args + typedef void * GC_PTR; +# else +# define GC_PROTO(args) () + typedef char * GC_PTR; +# endif + +# ifdef __cplusplus + extern "C" { +# endif + + +/* Define word and signed_word to be unsigned and signed types of the */ +/* size as char * or void *. There seems to be no way to do this */ +/* even semi-portably. The following is probably no better/worse */ +/* than almost anything else. */ +/* The ANSI standard suggests that size_t and ptr_diff_t might be */ +/* better choices. But those appear to have incorrect definitions */ +/* on may systems. Notably "typedef int size_t" seems to be both */ +/* frequent and WRONG. */ +typedef unsigned long GC_word; +typedef long GC_signed_word; + +/* Public read-only variables */ + +GC_API GC_word GC_gc_no;/* Counter incremented per collection. */ + /* Includes empty GCs at startup. */ + + +/* Public R/W variables */ + +GC_API GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)); + /* When there is insufficient memory to satisfy */ + /* an allocation request, we return */ + /* (*GC_oom_fn)(). By default this just */ + /* returns 0. */ + /* If it returns, it must return 0 or a valid */ + /* pointer to a previously allocated heap */ + /* object. */ + +GC_API int GC_find_leak; + /* Do not actually garbage collect, but simply */ + /* report inaccessible memory that was not */ + /* deallocated with GC_free. Initial value */ + /* is determined by FIND_LEAK macro. */ + +GC_API int GC_quiet; /* Disable statistics output. Only matters if */ + /* collector has been compiled with statistics */ + /* enabled. This involves a performance cost, */ + /* and is thus not the default. */ + +GC_API int GC_finalize_on_demand; + /* If nonzero, finalizers will only be run in */ + /* response to an eplit GC_invoke_finalizers */ + /* call. The default is determined by whether */ + /* the FINALIZE_ON_DEMAND macro is defined */ + /* when the collector is built. */ + +GC_API int GC_java_finalization; + /* Mark objects reachable from finalizable */ + /* objects in a separate postpass. This makes */ + /* it a bit safer to use non-topologically- */ + /* ordered finalization. Default value is */ + /* determined by JAVA_FINALIZATION macro. */ + +GC_API int GC_dont_gc; /* Dont collect unless explicitly requested, e.g. */ + /* because it's not safe. */ + +GC_API int GC_dont_expand; + /* Dont expand heap unless explicitly requested */ + /* or forced to. */ + +GC_API int GC_full_freq; /* Number of partial collections between */ + /* full collections. Matters only if */ + /* GC_incremental is set. */ + +GC_API GC_word GC_non_gc_bytes; + /* Bytes not considered candidates for collection. */ + /* Used only to control scheduling of collections. */ + +GC_API GC_word GC_free_space_divisor; + /* We try to make sure that we allocate at */ + /* least N/GC_free_space_divisor bytes between */ + /* collections, where N is the heap size plus */ + /* a rough estimate of the root set size. */ + /* Initially, GC_free_space_divisor = 4. */ + /* Increasing its value will use less space */ + /* but more collection time. Decreasing it */ + /* will appreciably decrease collection time */ + /* at the expense of space. */ + /* GC_free_space_divisor = 1 will effectively */ + /* disable collections. */ + +GC_API GC_word GC_max_retries; + /* The maximum number of GCs attempted before */ + /* reporting out of memory after heap */ + /* expansion fails. Initially 0. */ + + +GC_API char *GC_stackbottom; /* Cool end of user stack. */ + /* May be set in the client prior to */ + /* calling any GC_ routines. This */ + /* avoids some overhead, and */ + /* potentially some signals that can */ + /* confuse debuggers. Otherwise the */ + /* collector attempts to set it */ + /* automatically. */ + /* For multithreaded code, this is the */ + /* cold end of the stack for the */ + /* primordial thread. */ + +/* Public procedures */ +/* + * general purpose allocation routines, with roughly malloc calling conv. + * The atomic versions promise that no relevant pointers are contained + * in the object. The nonatomic versions guarantee that the new object + * is cleared. GC_malloc_stubborn promises that no changes to the object + * will occur after GC_end_stubborn_change has been called on the + * result of GC_malloc_stubborn. GC_malloc_uncollectable allocates an object + * that is scanned for pointers to collectable objects, but is not itself + * collectable. GC_malloc_uncollectable and GC_free called on the resulting + * object implicitly update GC_non_gc_bytes appropriately. + */ +GC_API GC_PTR GC_malloc GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_atomic GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_uncollectable GC_PROTO((size_t size_in_bytes)); +GC_API GC_PTR GC_malloc_stubborn GC_PROTO((size_t size_in_bytes)); + +/* The following is only defined if the library has been suitably */ +/* compiled: */ +GC_API GC_PTR GC_malloc_atomic_uncollectable GC_PROTO((size_t size_in_bytes)); + +/* Explicitly deallocate an object. Dangerous if used incorrectly. */ +/* Requires a pointer to the base of an object. */ +/* If the argument is stubborn, it should not be changeable when freed. */ +/* An object should not be enable for finalization when it is */ +/* explicitly deallocated. */ +/* GC_free(0) is a no-op, as required by ANSI C for free. */ +GC_API void GC_free GC_PROTO((GC_PTR object_addr)); + +/* + * Stubborn objects may be changed only if the collector is explicitly informed. + * The collector is implicitly informed of coming change when such + * an object is first allocated. The following routines inform the + * collector that an object will no longer be changed, or that it will + * once again be changed. Only nonNIL pointer stores into the object + * are considered to be changes. The argument to GC_end_stubborn_change + * must be exacly the value returned by GC_malloc_stubborn or passed to + * GC_change_stubborn. (In the second case it may be an interior pointer + * within 512 bytes of the beginning of the objects.) + * There is a performance penalty for allowing more than + * one stubborn object to be changed at once, but it is acceptable to + * do so. The same applies to dropping stubborn objects that are still + * changeable. + */ +GC_API void GC_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_end_stubborn_change GC_PROTO((GC_PTR)); + +/* Return a pointer to the base (lowest address) of an object given */ +/* a pointer to a location within the object. */ +/* Return 0 if displaced_pointer doesn't point to within a valid */ +/* object. */ +GC_API GC_PTR GC_base GC_PROTO((GC_PTR displaced_pointer)); + +/* Given a pointer to the base of an object, return its size in bytes. */ +/* The returned size may be slightly larger than what was originally */ +/* requested. */ +GC_API size_t GC_size GC_PROTO((GC_PTR object_addr)); + +/* For compatibility with C library. This is occasionally faster than */ +/* a malloc followed by a bcopy. But if you rely on that, either here */ +/* or with the standard C library, your code is broken. In my */ +/* opinion, it shouldn't have been invented, but now we're stuck. -HB */ +/* The resulting object has the same kind as the original. */ +/* If the argument is stubborn, the result will have changes enabled. */ +/* It is an error to have changes enabled for the original object. */ +/* Follows ANSI comventions for NULL old_object. */ +GC_API GC_PTR GC_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes)); + +/* Explicitly increase the heap size. */ +/* Returns 0 on failure, 1 on success. */ +GC_API int GC_expand_hp GC_PROTO((size_t number_of_bytes)); + +/* Limit the heap size to n bytes. Useful when you're debugging, */ +/* especially on systems that don't handle running out of memory well. */ +/* n == 0 ==> unbounded. This is the default. */ +GC_API void GC_set_max_heap_size GC_PROTO((GC_word n)); + +/* Inform the collector that a certain section of statically allocated */ +/* memory contains no pointers to garbage collected memory. Thus it */ +/* need not be scanned. This is sometimes important if the application */ +/* maps large read/write files into the address space, which could be */ +/* mistaken for dynamic library data segments on some systems. */ +GC_API void GC_exclude_static_roots GC_PROTO((GC_PTR start, GC_PTR finish)); + +/* Clear the set of root segments. Wizards only. */ +GC_API void GC_clear_roots GC_PROTO((void)); + +/* Add a root segment. Wizards only. */ +GC_API void GC_add_roots GC_PROTO((char * low_address, + char * high_address_plus_1)); + +/* Add a displacement to the set of those considered valid by the */ +/* collector. GC_register_displacement(n) means that if p was returned */ +/* by GC_malloc, then (char *)p + n will be considered to be a valid */ +/* pointer to n. N must be small and less than the size of p. */ +/* (All pointers to the interior of objects from the stack are */ +/* considered valid in any case. This applies to heap objects and */ +/* static data.) */ +/* Preferably, this should be called before any other GC procedures. */ +/* Calling it later adds to the probability of excess memory */ +/* retention. */ +/* This is a no-op if the collector was compiled with recognition of */ +/* arbitrary interior pointers enabled, which is now the default. */ +GC_API void GC_register_displacement GC_PROTO((GC_word n)); + +/* The following version should be used if any debugging allocation is */ +/* being done. */ +GC_API void GC_debug_register_displacement GC_PROTO((GC_word n)); + +/* Explicitly trigger a full, world-stop collection. */ +GC_API void GC_gcollect GC_PROTO((void)); + +/* Trigger a full world-stopped collection. Abort the collection if */ +/* and when stop_func returns a nonzero value. Stop_func will be */ +/* called frequently, and should be reasonably fast. This works even */ +/* if virtual dirty bits, and hence incremental collection is not */ +/* available for this architecture. Collections can be aborted faster */ +/* than normal pause times for incremental collection. However, */ +/* aborted collections do no useful work; the next collection needs */ +/* to start from the beginning. */ +/* Return 0 if the collection was aborted, 1 if it succeeded. */ +typedef int (* GC_stop_func) GC_PROTO((void)); +GC_API int GC_try_to_collect GC_PROTO((GC_stop_func stop_func)); + +/* Return the number of bytes in the heap. Excludes collector private */ +/* data structures. Includes empty blocks and fragmentation loss. */ +/* Includes some pages that were allocated but never written. */ +GC_API size_t GC_get_heap_size GC_PROTO((void)); + +/* Return the number of bytes allocated since the last collection. */ +GC_API size_t GC_get_bytes_since_gc GC_PROTO((void)); + +/* Enable incremental/generational collection. */ +/* Not advisable unless dirty bits are */ +/* available or most heap objects are */ +/* pointerfree(atomic) or immutable. */ +/* Don't use in leak finding mode. */ +/* Ignored if GC_dont_gc is true. */ +GC_API void GC_enable_incremental GC_PROTO((void)); + +/* Perform some garbage collection work, if appropriate. */ +/* Return 0 if there is no more work to be done. */ +/* Typically performs an amount of work corresponding roughly */ +/* to marking from one page. May do more work if further */ +/* progress requires it, e.g. if incremental collection is */ +/* disabled. It is reasonable to call this in a wait loop */ +/* until it returns 0. */ +GC_API int GC_collect_a_little GC_PROTO((void)); + +/* Allocate an object of size lb bytes. The client guarantees that */ +/* as long as the object is live, it will be referenced by a pointer */ +/* that points to somewhere within the first 256 bytes of the object. */ +/* (This should normally be declared volatile to prevent the compiler */ +/* from invalidating this assertion.) This routine is only useful */ +/* if a large array is being allocated. It reduces the chance of */ +/* accidentally retaining such an array as a result of scanning an */ +/* integer that happens to be an address inside the array. (Actually, */ +/* it reduces the chance of the allocator not finding space for such */ +/* an array, since it will try hard to avoid introducing such a false */ +/* reference.) On a SunOS 4.X or MS Windows system this is recommended */ +/* for arrays likely to be larger than 100K or so. For other systems, */ +/* or if the collector is not configured to recognize all interior */ +/* pointers, the threshold is normally much higher. */ +GC_API GC_PTR GC_malloc_ignore_off_page GC_PROTO((size_t lb)); +GC_API GC_PTR GC_malloc_atomic_ignore_off_page GC_PROTO((size_t lb)); + +#if defined(__sgi) && !defined(__GNUC__) && _COMPILER_VERSION >= 720 +# define GC_ADD_CALLER +# define GC_RETURN_ADDR (GC_word)__return_address +#endif + +#ifdef GC_ADD_CALLER +# define GC_EXTRAS GC_RETURN_ADDR, __FILE__, __LINE__ +# define GC_EXTRA_PARAMS GC_word ra, char * descr_string, int descr_int +#else +# define GC_EXTRAS __FILE__, __LINE__ +# define GC_EXTRA_PARAMS char * descr_string, int descr_int +#endif + +/* Debugging (annotated) allocation. GC_gcollect will check */ +/* objects allocated in this way for overwrites, etc. */ +GC_API GC_PTR GC_debug_malloc + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_atomic + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_uncollectable + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API GC_PTR GC_debug_malloc_stubborn + GC_PROTO((size_t size_in_bytes, GC_EXTRA_PARAMS)); +GC_API void GC_debug_free GC_PROTO((GC_PTR object_addr)); +GC_API GC_PTR GC_debug_realloc + GC_PROTO((GC_PTR old_object, size_t new_size_in_bytes, + GC_EXTRA_PARAMS)); + +GC_API void GC_debug_change_stubborn GC_PROTO((GC_PTR)); +GC_API void GC_debug_end_stubborn_change GC_PROTO((GC_PTR)); +# ifdef GC_DEBUG +# define GC_MALLOC(sz) GC_debug_malloc(sz, GC_EXTRAS) +# define GC_MALLOC_ATOMIC(sz) GC_debug_malloc_atomic(sz, GC_EXTRAS) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_debug_malloc_uncollectable(sz, \ + GC_EXTRAS) +# define GC_REALLOC(old, sz) GC_debug_realloc(old, sz, GC_EXTRAS) +# define GC_FREE(p) GC_debug_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_debug_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_debug_malloc_stubborn(sz, GC_EXTRAS); +# define GC_CHANGE_STUBBORN(p) GC_debug_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_debug_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, GC_base(obj)) +# define GC_REGISTER_DISPLACEMENT(n) GC_debug_register_displacement(n) +# else +# define GC_MALLOC(sz) GC_malloc(sz) +# define GC_MALLOC_ATOMIC(sz) GC_malloc_atomic(sz) +# define GC_MALLOC_UNCOLLECTABLE(sz) GC_malloc_uncollectable(sz) +# define GC_REALLOC(old, sz) GC_realloc(old, sz) +# define GC_FREE(p) GC_free(p) +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer(p, f, d, of, od) +# define GC_REGISTER_FINALIZER_IGNORE_SELF(p, f, d, of, od) \ + GC_register_finalizer_ignore_self(p, f, d, of, od) +# define GC_MALLOC_STUBBORN(sz) GC_malloc_stubborn(sz) +# define GC_CHANGE_STUBBORN(p) GC_change_stubborn(p) +# define GC_END_STUBBORN_CHANGE(p) GC_end_stubborn_change(p) +# define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link, obj) \ + GC_general_register_disappearing_link(link, obj) +# define GC_REGISTER_DISPLACEMENT(n) GC_register_displacement(n) +# endif +/* The following are included because they are often convenient, and */ +/* reduce the chance for a misspecifed size argument. But calls may */ +/* expand to something syntactically incorrect if t is a complicated */ +/* type expression. */ +# define GC_NEW(t) (t *)GC_MALLOC(sizeof (t)) +# define GC_NEW_ATOMIC(t) (t *)GC_MALLOC_ATOMIC(sizeof (t)) +# define GC_NEW_STUBBORN(t) (t *)GC_MALLOC_STUBBORN(sizeof (t)) +# define GC_NEW_UNCOLLECTABLE(t) (t *)GC_MALLOC_UNCOLLECTABLE(sizeof (t)) + +/* Finalization. Some of these primitives are grossly unsafe. */ +/* The idea is to make them both cheap, and sufficient to build */ +/* a safer layer, closer to PCedar finalization. */ +/* The interface represents my conclusions from a long discussion */ +/* with Alan Demers, Dan Greene, Carl Hauser, Barry Hayes, */ +/* Christian Jacobi, and Russ Atkinson. It's not perfect, and */ +/* probably nobody else agrees with it. Hans-J. Boehm 3/13/92 */ +typedef void (*GC_finalization_proc) + GC_PROTO((GC_PTR obj, GC_PTR client_data)); + +GC_API void GC_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + /* When obj is no longer accessible, invoke */ + /* (*fn)(obj, cd). If a and b are inaccessible, and */ + /* a points to b (after disappearing links have been */ + /* made to disappear), then only a will be */ + /* finalized. (If this does not create any new */ + /* pointers to b, then b will be finalized after the */ + /* next collection.) Any finalizable object that */ + /* is reachable from itself by following one or more */ + /* pointers will not be finalized (or collected). */ + /* Thus cycles involving finalizable objects should */ + /* be avoided, or broken by disappearing links. */ + /* All but the last finalizer registered for an object */ + /* is ignored. */ + /* Finalization may be removed by passing 0 as fn. */ + /* Finalizers are implicitly unregistered just before */ + /* they are invoked. */ + /* The old finalizer and client data are stored in */ + /* *ofn and *ocd. */ + /* Fn is never invoked on an accessible object, */ + /* provided hidden pointers are converted to real */ + /* pointers only if the allocation lock is held, and */ + /* such conversions are not performed by finalization */ + /* routines. */ + /* If GC_register_finalizer is aborted as a result of */ + /* a signal, the object may be left with no */ + /* finalization, even if neither the old nor new */ + /* finalizer were NULL. */ + /* Obj should be the nonNULL starting address of an */ + /* object allocated by GC_malloc or friends. */ + /* Note that any garbage collectable object referenced */ + /* by cd will be considered accessible until the */ + /* finalizer is invoked. */ + +/* Another versions of the above follow. It ignores */ +/* self-cycles, i.e. pointers from a finalizable object to */ +/* itself. There is a stylistic argument that this is wrong, */ +/* but it's unavoidable for C++, since the compiler may */ +/* silently introduce these. It's also benign in that specific */ +/* case. */ +GC_API void GC_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); +GC_API void GC_debug_register_finalizer_ignore_self + GC_PROTO((GC_PTR obj, GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR *ocd)); + +/* The following routine may be used to break cycles between */ +/* finalizable objects, thus causing cyclic finalizable */ +/* objects to be finalized in the correct order. Standard */ +/* use involves calling GC_register_disappearing_link(&p), */ +/* where p is a pointer that is not followed by finalization */ +/* code, and should not be considered in determining */ +/* finalization order. */ +GC_API int GC_register_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Link should point to a field of a heap allocated */ + /* object obj. *link will be cleared when obj is */ + /* found to be inaccessible. This happens BEFORE any */ + /* finalization code is invoked, and BEFORE any */ + /* decisions about finalization order are made. */ + /* This is useful in telling the finalizer that */ + /* some pointers are not essential for proper */ + /* finalization. This may avoid finalization cycles. */ + /* Note that obj may be resurrected by another */ + /* finalizer, and thus the clearing of *link may */ + /* be visible to non-finalization code. */ + /* There's an argument that an arbitrary action should */ + /* be allowed here, instead of just clearing a pointer. */ + /* But this causes problems if that action alters, or */ + /* examines connectivity. */ + /* Returns 1 if link was already registered, 0 */ + /* otherwise. */ + /* Only exists for backward compatibility. See below: */ + +GC_API int GC_general_register_disappearing_link + GC_PROTO((GC_PTR * /* link */, GC_PTR obj)); + /* A slight generalization of the above. *link is */ + /* cleared when obj first becomes inaccessible. This */ + /* can be used to implement weak pointers easily and */ + /* safely. Typically link will point to a location */ + /* holding a disguised pointer to obj. (A pointer */ + /* inside an "atomic" object is effectively */ + /* disguised.) In this way soft */ + /* pointers are broken before any object */ + /* reachable from them are finalized. Each link */ + /* May be registered only once, i.e. with one obj */ + /* value. This was added after a long email discussion */ + /* with John Ellis. */ + /* Obj must be a pointer to the first word of an object */ + /* we allocated. It is unsafe to explicitly deallocate */ + /* the object containing link. Explicitly deallocating */ + /* obj may or may not cause link to eventually be */ + /* cleared. */ +GC_API int GC_unregister_disappearing_link GC_PROTO((GC_PTR * /* link */)); + /* Returns 0 if link was not actually registered. */ + /* Undoes a registration by either of the above two */ + /* routines. */ + +/* Auxiliary fns to make finalization work correctly with displaced */ +/* pointers introduced by the debugging allocators. */ +GC_API GC_PTR GC_make_closure GC_PROTO((GC_finalization_proc fn, GC_PTR data)); +GC_API void GC_debug_invoke_finalizer GC_PROTO((GC_PTR obj, GC_PTR data)); + +GC_API int GC_invoke_finalizers GC_PROTO((void)); + /* Run finalizers for all objects that are ready to */ + /* be finalized. Return the number of finalizers */ + /* that were run. Normally this is also called */ + /* implicitly during some allocations. If */ + /* GC-finalize_on_demand is nonzero, it must be called */ + /* explicitly. */ + +/* GC_set_warn_proc can be used to redirect or filter warning messages. */ +/* p may not be a NULL pointer. */ +typedef void (*GC_warn_proc) GC_PROTO((char *msg, GC_word arg)); +GC_API GC_warn_proc GC_set_warn_proc GC_PROTO((GC_warn_proc p)); + /* Returns old warning procedure. */ + +/* The following is intended to be used by a higher level */ +/* (e.g. cedar-like) finalization facility. It is expected */ +/* that finalization code will arrange for hidden pointers to */ +/* disappear. Otherwise objects can be accessed after they */ +/* have been collected. */ +/* Note that putting pointers in atomic objects or in */ +/* nonpointer slots of "typed" objects is equivalent to */ +/* disguising them in this way, and may have other advantages. */ +# if defined(I_HIDE_POINTERS) || defined(GC_I_HIDE_POINTERS) + typedef GC_word GC_hidden_pointer; +# define HIDE_POINTER(p) (~(GC_hidden_pointer)(p)) +# define REVEAL_POINTER(p) ((GC_PTR)(HIDE_POINTER(p))) + /* Converting a hidden pointer to a real pointer requires verifying */ + /* that the object still exists. This involves acquiring the */ + /* allocator lock to avoid a race with the collector. */ +# endif /* I_HIDE_POINTERS */ + +typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data)); +GC_API GC_PTR GC_call_with_alloc_lock + GC_PROTO((GC_fn_type fn, GC_PTR client_data)); + +/* Check that p and q point to the same object. */ +/* Fail conspicuously if they don't. */ +/* Returns the first argument. */ +/* Succeeds if neither p nor q points to the heap. */ +/* May succeed if both p and q point to between heap objects. */ +GC_API GC_PTR GC_same_obj GC_PROTO((GC_PTR p, GC_PTR q)); + +/* Checked pointer pre- and post- increment operations. Note that */ +/* the second argument is in units of bytes, not multiples of the */ +/* object size. This should either be invoked from a macro, or the */ +/* call should be automatically generated. */ +GC_API GC_PTR GC_pre_incr GC_PROTO((GC_PTR *p, size_t how_much)); +GC_API GC_PTR GC_post_incr GC_PROTO((GC_PTR *p, size_t how_much)); + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't fail conspicuously. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for multithreaded worlds. */ +GC_API GC_PTR GC_is_visible GC_PROTO((GC_PTR p)); + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Fail conspicuously if this property does not hold. */ +/* Uninteresting with ALL_INTERIOR_POINTERS. */ +/* Always returns its argument. */ +GC_API GC_PTR GC_is_valid_displacement GC_PROTO((GC_PTR p)); + +/* Safer, but slow, pointer addition. Probably useful mainly with */ +/* a preprocessor. Useful only for heap pointers. */ +#ifdef GC_DEBUG +# define GC_PTR_ADD3(x, n, type_of_result) \ + ((type_of_result)GC_same_obj((x)+(n), (x))) +# define GC_PRE_INCR3(x, n, type_of_result) \ + ((type_of_result)GC_pre_incr(&(x), (n)*sizeof(*x)) +# define GC_POST_INCR2(x, type_of_result) \ + ((type_of_result)GC_post_incr(&(x), sizeof(*x)) +# ifdef __GNUC__ +# define GC_PTR_ADD(x, n) \ + GC_PTR_ADD3(x, n, typeof(x)) +# define GC_PRE_INCR(x, n) \ + GC_PRE_INCR3(x, n, typeof(x)) +# define GC_POST_INCR(x, n) \ + GC_POST_INCR3(x, typeof(x)) +# else + /* We can't do this right without typeof, which ANSI */ + /* decided was not sufficiently useful. Repeatedly */ + /* mentioning the arguments seems too dangerous to be */ + /* useful. So does not casting the result. */ +# define GC_PTR_ADD(x, n) ((x)+(n)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_ADD3(x, n, type_of_result) ((x)+(n)) +# define GC_PTR_ADD(x, n) ((x)+(n)) +# define GC_PRE_INCR3(x, n, type_of_result) ((x) += (n)) +# define GC_PRE_INCR(x, n) ((x) += (n)) +# define GC_POST_INCR2(x, n, type_of_result) ((x)++) +# define GC_POST_INCR(x, n) ((x)++) +#endif + +/* Safer assignment of a pointer to a nonstack location. */ +#ifdef GC_DEBUG +# ifdef __STDC__ +# define GC_PTR_STORE(p, q) \ + (*(void **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# else +# define GC_PTR_STORE(p, q) \ + (*(char **)GC_is_visible(p) = GC_is_valid_displacement(q)) +# endif +#else /* !GC_DEBUG */ +# define GC_PTR_STORE(p, q) *((p) = (q)) +#endif + +/* Fynctions called to report pointer checking errors */ +GC_API void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR p, GC_PTR q)); + +GC_API void (*GC_is_valid_displacement_print_proc) + GC_PROTO((GC_PTR p)); + +GC_API void (*GC_is_visible_print_proc) + GC_PROTO((GC_PTR p)); + +#if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +#endif + +#ifdef SOLARIS_THREADS +/* We need to intercept calls to many of the threads primitives, so */ +/* that we can locate thread stacks and stop the world. */ +/* Note also that the collector cannot see thread specific data. */ +/* Thread specific data should generally consist of pointers to */ +/* uncollectable objects, which are deallocated using the destructor */ +/* facility in thr_keycreate. */ +# include <thread.h> +# include <signal.h> + int GC_thr_create(void *stack_base, size_t stack_size, + void *(*start_routine)(void *), void *arg, long flags, + thread_t *new_thread); + int GC_thr_join(thread_t wait_for, thread_t *departed, void **status); + int GC_thr_suspend(thread_t target_thread); + int GC_thr_continue(thread_t target_thread); + void * GC_dlopen(const char *path, int mode); + +# ifdef _SOLARIS_PTHREADS +# include <pthread.h> + extern int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void * (*thread_execp)(void *), void *arg); + extern int GC_pthread_join(pthread_t wait_for, void **status); + +# undef thread_t + +# define pthread_join GC_pthread_join +# define pthread_create GC_pthread_create +#endif + +# define thr_create GC_thr_create +# define thr_join GC_thr_join +# define thr_suspend GC_thr_suspend +# define thr_continue GC_thr_continue +# define dlopen GC_dlopen + +# endif /* SOLARIS_THREADS */ + + +#if defined(IRIX_THREADS) || defined(LINUX_THREADS) +/* We treat these similarly. */ +# include <pthread.h> +# include <signal.h> + + int GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); + int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset); + int GC_pthread_join(pthread_t thread, void **retval); + +# define pthread_create GC_pthread_create +# define pthread_sigmask GC_pthread_sigmask +# define pthread_join GC_pthread_join + +#endif /* IRIX_THREADS || LINUX_THREADS */ + +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) + /* Any flavor of threads except SRC_M3. */ +/* This returns a list of objects, linked through their first */ +/* word. Its use can greatly reduce lock contention problems, since */ +/* the allocation lock can be acquired and released many fewer times. */ +/* lb must be large enough to hold the pointer field. */ +GC_PTR GC_malloc_many(size_t lb); +#define GC_NEXT(p) (*(GC_PTR *)(p)) /* Retrieve the next element */ + /* in returned list. */ +extern void GC_thr_init(); /* Needed for Solaris/X86 */ + +#endif /* THREADS && !SRC_M3 */ + +/* + * If you are planning on putting + * the collector in a SunOS 5 dynamic library, you need to call GC_INIT() + * from the statically loaded program section. + * This circumvents a Solaris 2.X (X<=4) linker bug. + */ +#if defined(sparc) || defined(__sparc) +# define GC_INIT() { extern end, etext; \ + GC_noop(&end, &etext); } +#else +# if defined(__CYGWIN32__) && defined(GC_USE_DLL) + /* + * Similarly gnu-win32 DLLs need explicit initialization + */ +# define GC_INIT() { GC_add_roots(DATASTART, DATAEND); } +# else +# define GC_INIT() +# endif +#endif + +#if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) + /* win32S may not free all resources on process exit. */ + /* This explicitly deallocates the heap. */ + GC_API void GC_win32_free_heap (); +#endif + +#ifdef __cplusplus + } /* end of extern "C" */ +#endif + +#endif /* _GC_H */ diff --git a/gc/include/gc_alloc.h b/gc/include/gc_alloc.h new file mode 100644 index 0000000..1f1d54a --- /dev/null +++ b/gc/include/gc_alloc.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +// +// This is a C++ header file that is intended to replace the SGI STL +// alloc.h. This assumes SGI STL version < 3.0. +// +// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE +// and -DALL_INTERIOR_POINTERS. We also recommend +// -DREDIRECT_MALLOC=GC_uncollectable_malloc. +// +// Some of this could be faster in the explicit deallocation case. In particular, +// we spend too much time clearing objects on the free lists. That could be avoided. +// +// This uses template classes with static members, and hence does not work +// with g++ 2.7.2 and earlier. +// + +#include "gc.h" + +#ifndef GC_ALLOC_H + +#define GC_ALLOC_H +#define __ALLOC_H // Prevent inclusion of the default version. Ugly. +#define __SGI_STL_ALLOC_H +#define __SGI_STL_INTERNAL_ALLOC_H + +#ifndef __ALLOC +# define __ALLOC alloc +#endif + +#include <stddef.h> +#include <string.h> + +// The following is just replicated from the conventional SGI alloc.h: + +template<class T, class alloc> +class simple_alloc { + +public: + static T *allocate(size_t n) + { return 0 == n? 0 : (T*) alloc::allocate(n * sizeof (T)); } + static T *allocate(void) + { return (T*) alloc::allocate(sizeof (T)); } + static void deallocate(T *p, size_t n) + { if (0 != n) alloc::deallocate(p, n * sizeof (T)); } + static void deallocate(T *p) + { alloc::deallocate(p, sizeof (T)); } +}; + +#include "gc.h" + +// The following need to match collector data structures. +// We can't include gc_priv.h, since that pulls in way too much stuff. +// This should eventually be factored out into another include file. + +extern "C" { + extern void ** const GC_objfreelist_ptr; + extern void ** const GC_aobjfreelist_ptr; + extern void ** const GC_uobjfreelist_ptr; + extern void ** const GC_auobjfreelist_ptr; + + extern void GC_incr_words_allocd(size_t words); + extern void GC_incr_mem_freed(size_t words); + + extern char * GC_generic_malloc_words_small(size_t word, int kind); +} + +// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and +// AUNCOLLECTABLE in gc_priv.h. + +enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2, + GC_AUNCOLLECTABLE = 3 }; + +enum { GC_max_fast_bytes = 255 }; + +enum { GC_bytes_per_word = sizeof(char *) }; + +enum { GC_byte_alignment = 8 }; + +enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word }; + +inline void * &GC_obj_link(void * p) +{ return *(void **)p; } + +// Compute a number of words >= n+1 bytes. +// The +1 allows for pointers one past the end. +inline size_t GC_round_up(size_t n) +{ + return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment; +} + +// The same but don't allow for extra byte. +inline size_t GC_round_up_uncollectable(size_t n) +{ + return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment; +} + +template <int dummy> +class GC_aux_template { +public: + // File local count of allocated words. Occasionally this is + // added into the global count. A separate count is necessary since the + // real one must be updated with a procedure call. + static size_t GC_words_recently_allocd; + + // Same for uncollectable mmory. Not yet reflected in either + // GC_words_recently_allocd or GC_non_gc_bytes. + static size_t GC_uncollectable_words_recently_allocd; + + // Similar counter for explicitly deallocated memory. + static size_t GC_mem_recently_freed; + + // Again for uncollectable memory. + static size_t GC_uncollectable_mem_recently_freed; + + static void * GC_out_of_line_malloc(size_t nwords, int kind); +}; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_mem_recently_freed = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_mem_recently_freed = 0; + +template <int dummy> +void * GC_aux_template<dummy>::GC_out_of_line_malloc(size_t nwords, int kind) +{ + GC_words_recently_allocd += GC_uncollectable_words_recently_allocd; + GC_non_gc_bytes += + GC_bytes_per_word * GC_uncollectable_words_recently_allocd; + GC_uncollectable_words_recently_allocd = 0; + + GC_mem_recently_freed += GC_uncollectable_mem_recently_freed; + GC_non_gc_bytes -= + GC_bytes_per_word * GC_uncollectable_mem_recently_freed; + GC_uncollectable_mem_recently_freed = 0; + + GC_incr_words_allocd(GC_words_recently_allocd); + GC_words_recently_allocd = 0; + + GC_incr_mem_freed(GC_mem_recently_freed); + GC_mem_recently_freed = 0; + + return GC_generic_malloc_words_small(nwords, kind); +} + +typedef GC_aux_template<0> GC_aux; + +// A fast, single-threaded, garbage-collected allocator +// We assume the first word will be immediately overwritten. +// In this version, deallocation is not a noop, and explicit +// deallocation is likely to help performance. +template <int dummy> +class single_client_gc_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc(n); + flh = GC_objfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + flh = GC_aobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_objfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + memset((char *)p + GC_bytes_per_word, 0, + GC_bytes_per_word * (nwords - 1)); + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_aobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_gc_alloc_template<0> single_client_gc_alloc; + +// Once more, for uncollectable objects. +template <int dummy> +class single_client_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + flh = GC_uobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + flh = GC_auobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_uobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_auobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_alloc_template<0> single_client_alloc; + +template < int dummy > +class gc_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic(n); } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } +}; + +typedef gc_alloc_template < 0 > gc_alloc; + +template < int dummy > +class alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic_uncollectable(n); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } +}; + +typedef alloc_template < 0 > alloc; + +#ifdef _SGI_SOURCE + +// We want to specialize simple_alloc so that it does the right thing +// for all pointerfree types. At the moment there is no portable way to +// even approximate that. The following approximation should work for +// SGI compilers, and perhaps some others. + +# define __GC_SPECIALIZE(T,alloc) \ +class simple_alloc<T, alloc> { \ +public: \ + static T *allocate(size_t n) \ + { return 0 == n? 0 : \ + (T*) alloc::ptr_free_allocate(n * sizeof (T)); } \ + static T *allocate(void) \ + { return (T*) alloc::ptr_free_allocate(sizeof (T)); } \ + static void deallocate(T *p, size_t n) \ + { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof (T)); } \ + static void deallocate(T *p) \ + { alloc::ptr_free_deallocate(p, sizeof (T)); } \ +}; + +__GC_SPECIALIZE(char, gc_alloc) +__GC_SPECIALIZE(int, gc_alloc) +__GC_SPECIALIZE(unsigned, gc_alloc) +__GC_SPECIALIZE(float, gc_alloc) +__GC_SPECIALIZE(double, gc_alloc) + +__GC_SPECIALIZE(char, alloc) +__GC_SPECIALIZE(int, alloc) +__GC_SPECIALIZE(unsigned, alloc) +__GC_SPECIALIZE(float, alloc) +__GC_SPECIALIZE(double, alloc) + +__GC_SPECIALIZE(char, single_client_gc_alloc) +__GC_SPECIALIZE(int, single_client_gc_alloc) +__GC_SPECIALIZE(unsigned, single_client_gc_alloc) +__GC_SPECIALIZE(float, single_client_gc_alloc) +__GC_SPECIALIZE(double, single_client_gc_alloc) + +__GC_SPECIALIZE(char, single_client_alloc) +__GC_SPECIALIZE(int, single_client_alloc) +__GC_SPECIALIZE(unsigned, single_client_alloc) +__GC_SPECIALIZE(float, single_client_alloc) +__GC_SPECIALIZE(double, single_client_alloc) + +#ifdef __STL_USE_STD_ALLOCATORS + +???copy stuff from stl_alloc.h or remove it to a different file ??? + +#endif /* __STL_USE_STD_ALLOCATORS */ + +#endif /* _SGI_SOURCE */ + +#endif /* GC_ALLOC_H */ diff --git a/gc/include/gc_cpp.h b/gc/include/gc_cpp.h new file mode 100644 index 0000000..ad7df5d --- /dev/null +++ b/gc/include/gc_cpp.h @@ -0,0 +1,290 @@ +#ifndef GC_CPP_H +#define GC_CPP_H +/**************************************************************************** +Copyright (c) 1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program for any +purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is +granted, provided the above notices are retained, and a notice that +the code was modified is included with the above copyright notice. +**************************************************************************** + +C++ Interface to the Boehm Collector + + John R. Ellis and Jesse Hull + Last modified on Mon Jul 24 15:43:42 PDT 1995 by ellis + +This interface provides access to the Boehm collector. It provides +basic facilities similar to those described in "Safe, Efficient +Garbage Collection for C++", by John R. Elis and David L. Detlefs +(ftp.parc.xerox.com:/pub/ellis/gc). + +All heap-allocated objects are either "collectable" or +"uncollectable". Programs must explicitly delete uncollectable +objects, whereas the garbage collector will automatically delete +collectable objects when it discovers them to be inaccessible. +Collectable objects may freely point at uncollectable objects and vice +versa. + +Objects allocated with the built-in "::operator new" are uncollectable. + +Objects derived from class "gc" are collectable. For example: + + class A: public gc {...}; + A* a = new A; // a is collectable. + +Collectable instances of non-class types can be allocated using the GC +placement: + + typedef int A[ 10 ]; + A* a = new (GC) A; + +Uncollectable instances of classes derived from "gc" can be allocated +using the NoGC placement: + + class A: public gc {...}; + A* a = new (NoGC) A; // a is uncollectable. + +Both uncollectable and collectable objects can be explicitly deleted +with "delete", which invokes an object's destructors and frees its +storage immediately. + +A collectable object may have a clean-up function, which will be +invoked when the collector discovers the object to be inaccessible. +An object derived from "gc_cleanup" or containing a member derived +from "gc_cleanup" has a default clean-up function that invokes the +object's destructors. Explicit clean-up functions may be specified as +an additional placement argument: + + A* a = ::new (GC, MyCleanup) A; + +An object is considered "accessible" by the collector if it can be +reached by a path of pointers from static variables, automatic +variables of active functions, or from some object with clean-up +enabled; pointers from an object to itself are ignored. + +Thus, if objects A and B both have clean-up functions, and A points at +B, B is considered accessible. After A's clean-up is invoked and its +storage released, B will then become inaccessible and will have its +clean-up invoked. If A points at B and B points to A, forming a +cycle, then that's considered a storage leak, and neither will be +collectable. See the interface gc.h for low-level facilities for +handling such cycles of objects with clean-up. + +The collector cannot guarrantee that it will find all inaccessible +objects. In practice, it finds almost all of them. + + +Cautions: + +1. Be sure the collector has been augmented with "make c++". + +2. If your compiler supports the new "operator new[]" syntax, then +add -DOPERATOR_NEW_ARRAY to the Makefile. + +If your compiler doesn't support "operator new[]", beware that an +array of type T, where T is derived from "gc", may or may not be +allocated as a collectable object (it depends on the compiler). Use +the explicit GC placement to make the array collectable. For example: + + class A: public gc {...}; + A* a1 = new A[ 10 ]; // collectable or uncollectable? + A* a2 = new (GC) A[ 10 ]; // collectable + +3. The destructors of collectable arrays of objects derived from +"gc_cleanup" will not be invoked properly. For example: + + class A: public gc_cleanup {...}; + A* a = new (GC) A[ 10 ]; // destructors not invoked correctly + +Typically, only the destructor for the first element of the array will +be invoked when the array is garbage-collected. To get all the +destructors of any array executed, you must supply an explicit +clean-up function: + + A* a = new (GC, MyCleanUp) A[ 10 ]; + +(Implementing clean-up of arrays correctly, portably, and in a way +that preserves the correct exception semantics requires a language +extension, e.g. the "gc" keyword.) + +4. Compiler bugs: + +* Solaris 2's CC (SC3.0) doesn't implement t->~T() correctly, so the +destructors of classes derived from gc_cleanup won't be invoked. +You'll have to explicitly register a clean-up function with +new-placement syntax. + +* Evidently cfront 3.0 does not allow destructors to be explicitly +invoked using the ANSI-conforming syntax t->~T(). If you're using +cfront 3.0, you'll have to comment out the class gc_cleanup, which +uses explicit invocation. + +****************************************************************************/ + +#include "gc.h" + +#ifndef THINK_CPLUS +#define _cdecl +#endif + +#if ! defined( OPERATOR_NEW_ARRAY ) \ + && (__BORLANDC__ >= 0x450 || (__GNUC__ >= 2 && __GNUC_MINOR__ >= 6) \ + || __WATCOMC__ >= 1050) +# define OPERATOR_NEW_ARRAY +#endif + +enum GCPlacement {GC, NoGC, PointerFreeGC}; + +class gc {public: + inline void* operator new( size_t size ); + inline void* operator new( size_t size, GCPlacement gcp ); + inline void operator delete( void* obj ); + +#ifdef OPERATOR_NEW_ARRAY + inline void* operator new[]( size_t size ); + inline void* operator new[]( size_t size, GCPlacement gcp ); + inline void operator delete[]( void* obj ); +#endif /* OPERATOR_NEW_ARRAY */ + }; + /* + Instances of classes derived from "gc" will be allocated in the + collected heap by default, unless an explicit NoGC placement is + specified. */ + +class gc_cleanup: virtual public gc {public: + inline gc_cleanup(); + inline virtual ~gc_cleanup(); +private: + inline static void _cdecl cleanup( void* obj, void* clientData );}; + /* + Instances of classes derived from "gc_cleanup" will be allocated + in the collected heap by default. When the collector discovers an + inaccessible object derived from "gc_cleanup" or containing a + member derived from "gc_cleanup", its destructors will be + invoked. */ + +extern "C" {typedef void (*GCCleanUpFunc)( void* obj, void* clientData );} + +inline void* operator new( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup = 0, + void* clientData = 0 ); + /* + Allocates a collectable or uncollected object, according to the + value of "gcp". + + For collectable objects, if "cleanup" is non-null, then when the + allocated object "obj" becomes inaccessible, the collector will + invoke the function "cleanup( obj, clientData )" but will not + invoke the object's destructors. It is an error to explicitly + delete an object allocated with a non-null "cleanup". + + It is an error to specify a non-null "cleanup" with NoGC or for + classes derived from "gc_cleanup" or containing members derived + from "gc_cleanup". */ + +#ifdef OPERATOR_NEW_ARRAY + +inline void* operator new[]( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup = 0, + void* clientData = 0 ); + /* + The operator new for arrays, identical to the above. */ + +#endif /* OPERATOR_NEW_ARRAY */ + +/**************************************************************************** + +Inline implementation + +****************************************************************************/ + +inline void* gc::operator new( size_t size ) { + return GC_MALLOC( size );} + +inline void* gc::operator new( size_t size, GCPlacement gcp ) { + if (gcp == GC) + return GC_MALLOC( size ); + else if (gcp == PointerFreeGC) + return GC_MALLOC_ATOMIC( size ); + else + return GC_MALLOC_UNCOLLECTABLE( size );} + +inline void gc::operator delete( void* obj ) { + GC_FREE( obj );} + + +#ifdef OPERATOR_NEW_ARRAY + +inline void* gc::operator new[]( size_t size ) { + return gc::operator new( size );} + +inline void* gc::operator new[]( size_t size, GCPlacement gcp ) { + return gc::operator new( size, gcp );} + +inline void gc::operator delete[]( void* obj ) { + gc::operator delete( obj );} + +#endif /* OPERATOR_NEW_ARRAY */ + + +inline gc_cleanup::~gc_cleanup() { + GC_REGISTER_FINALIZER_IGNORE_SELF( GC_base(this), 0, 0, 0, 0 );} + +inline void gc_cleanup::cleanup( void* obj, void* displ ) { + ((gc_cleanup*) ((char*) obj + (ptrdiff_t) displ))->~gc_cleanup();} + +inline gc_cleanup::gc_cleanup() { + GC_finalization_proc oldProc; + void* oldData; + void* base = GC_base( (void *) this ); + if (0 == base) return; + GC_REGISTER_FINALIZER_IGNORE_SELF( + base, cleanup, (void*) ((char*) this - (char*) base), + &oldProc, &oldData ); + if (0 != oldProc) { + GC_REGISTER_FINALIZER_IGNORE_SELF( base, oldProc, oldData, 0, 0 );}} + +inline void* operator new( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup, + void* clientData ) +{ + void* obj; + + if (gcp == GC) { + obj = GC_MALLOC( size ); + if (cleanup != 0) + GC_REGISTER_FINALIZER_IGNORE_SELF( + obj, cleanup, clientData, 0, 0 );} + else if (gcp == PointerFreeGC) { + obj = GC_MALLOC_ATOMIC( size );} + else { + obj = GC_MALLOC_UNCOLLECTABLE( size );}; + return obj;} + + +#ifdef OPERATOR_NEW_ARRAY + +inline void* operator new[]( + size_t size, + GCPlacement gcp, + GCCleanUpFunc cleanup, + void* clientData ) +{ + return ::operator new( size, gcp, cleanup, clientData );} + +#endif /* OPERATOR_NEW_ARRAY */ + + +#endif /* GC_CPP_H */ + diff --git a/gc/include/gc_inl.h b/gc/include/gc_inl.h new file mode 100644 index 0000000..700843b --- /dev/null +++ b/gc/include/gc_inl.h @@ -0,0 +1,103 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, October 3, 1995 2:07 pm PDT */ + +# ifndef GC_PRIVATE_H +# include "private/gc_priv.h" +# endif + +/* USE OF THIS FILE IS NOT RECOMMENDED unless the collector has been */ +/* compiled without -DALL_INTERIOR_POINTERS or with */ +/* -DDONT_ADD_BYTE_AT_END, or the specified size includes a pointerfree */ +/* word at the end. In the standard collector configuration, */ +/* the final word of each object may not be scanned. */ +/* This is most useful for compilers that generate C. */ +/* Manual use is hereby discouraged. */ + +/* Allocate n words (NOT BYTES). X is made to point to the result. */ +/* It is assumed that n < MAXOBJSZ, and */ +/* that n > 0. On machines requiring double word alignment of some */ +/* data, we also assume that n is 1 or even. This bypasses the */ +/* MERGE_SIZES mechanism. In order to minimize the number of distinct */ +/* free lists that are maintained, the caller should ensure that a */ +/* small number of distinct values of n are used. (The MERGE_SIZES */ +/* mechanism normally does this by ensuring that only the leading three */ +/* bits of n may be nonzero. See misc.c for details.) We really */ +/* recommend this only in cases in which n is a constant, and no */ +/* locking is required. */ +/* In that case it may allow the compiler to perform substantial */ +/* additional optimizations. */ +# define GC_MALLOC_WORDS(result,n) \ +{ \ + register ptr_t op; \ + register ptr_t *opp; \ + DCL_LOCK_STATE; \ + \ + opp = &(GC_objfreelist[n]); \ + FASTLOCK(); \ + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { \ + FASTUNLOCK(); \ + (result) = GC_generic_malloc_words_small((n), NORMAL); \ + } else { \ + *opp = obj_link(op); \ + obj_link(op) = 0; \ + GC_words_allocd += (n); \ + FASTUNLOCK(); \ + (result) = (GC_PTR) op; \ + } \ +} + + +/* The same for atomic objects: */ +# define GC_MALLOC_ATOMIC_WORDS(result,n) \ +{ \ + register ptr_t op; \ + register ptr_t *opp; \ + DCL_LOCK_STATE; \ + \ + opp = &(GC_aobjfreelist[n]); \ + FASTLOCK(); \ + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { \ + FASTUNLOCK(); \ + (result) = GC_generic_malloc_words_small((n), PTRFREE); \ + } else { \ + *opp = obj_link(op); \ + obj_link(op) = 0; \ + GC_words_allocd += (n); \ + FASTUNLOCK(); \ + (result) = (GC_PTR) op; \ + } \ +} + +/* And once more for two word initialized objects: */ +# define GC_CONS(result, first, second) \ +{ \ + register ptr_t op; \ + register ptr_t *opp; \ + DCL_LOCK_STATE; \ + \ + opp = &(GC_objfreelist[2]); \ + FASTLOCK(); \ + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { \ + FASTUNLOCK(); \ + op = GC_generic_malloc_words_small(2, NORMAL); \ + } else { \ + *opp = obj_link(op); \ + GC_words_allocd += 2; \ + FASTUNLOCK(); \ + } \ + ((word *)op)[0] = (word)(first); \ + ((word *)op)[1] = (word)(second); \ + (result) = (GC_PTR) op; \ +} diff --git a/gc/include/gc_inline.h b/gc/include/gc_inline.h new file mode 100644 index 0000000..db62d1d --- /dev/null +++ b/gc/include/gc_inline.h @@ -0,0 +1 @@ +# include "gc_inl.h" diff --git a/gc/include/gc_typed.h b/gc/include/gc_typed.h new file mode 100644 index 0000000..e4a6b94 --- /dev/null +++ b/gc/include/gc_typed.h @@ -0,0 +1,91 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright 1996 Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Some simple primitives for allocation with explicit type information. + * Facilities for dynamic type inference may be added later. + * Should be used only for extremely performance critical applications, + * or if conservative collector leakage is otherwise a problem (unlikely). + * Note that this is implemented completely separately from the rest + * of the collector, and is not linked in unless referenced. + * This does not currently support GC_DEBUG in any interesting way. + */ +/* Boehm, May 19, 1994 2:13 pm PDT */ + +#ifndef _GC_TYPED_H +# define _GC_TYPED_H +# ifndef _GC_H +# include "gc.h" +# endif + +typedef GC_word * GC_bitmap; + /* The least significant bit of the first word is one if */ + /* the first word in the object may be a pointer. */ + +# define GC_get_bit(bm, index) \ + (((bm)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define GC_set_bit(bm, index) \ + (bm)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) + +typedef GC_word GC_descr; + +GC_API GC_descr GC_make_descriptor GC_PROTO((GC_bitmap bm, size_t len)); + /* Return a type descriptor for the object whose layout */ + /* is described by the argument. */ + /* The least significant bit of the first word is one */ + /* if the first word in the object may be a pointer. */ + /* The second argument specifies the number of */ + /* meaningful bits in the bitmap. The actual object */ + /* may be larger (but not smaller). Any additional */ + /* words in the object are assumed not to contain */ + /* pointers. */ + /* Returns a conservative approximation in the */ + /* (unlikely) case of insufficient memory to build */ + /* the descriptor. Calls to GC_make_descriptor */ + /* may consume some amount of a finite resource. This */ + /* is intended to be called once per type, not once */ + /* per allocation. */ + +GC_API GC_PTR GC_malloc_explicitly_typed + GC_PROTO((size_t size_in_bytes, GC_descr d)); + /* Allocate an object whose layout is described by d. */ + /* The resulting object MAY NOT BE PASSED TO REALLOC. */ + +GC_API GC_PTR GC_malloc_explicitly_typed_ignore_off_page + GC_PROTO((size_t size_in_bytes, GC_descr d)); + +GC_API GC_PTR GC_calloc_explicitly_typed + GC_PROTO((size_t nelements, + size_t element_size_in_bytes, + GC_descr d)); + /* Allocate an array of nelements elements, each of the */ + /* given size, and with the given descriptor. */ + /* The elemnt size must be a multiple of the byte */ + /* alignment required for pointers. E.g. on a 32-bit */ + /* machine with 16-bit aligned pointers, size_in_bytes */ + /* must be a multiple of 2. */ + +#ifdef GC_DEBUG +# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) GC_MALLOC(bytes) +# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) GC_MALLOC(n*bytes) +#else +# define GC_MALLOC_EXPLICTLY_TYPED(bytes, d) \ + GC_malloc_explicitly_typed(bytes, d) +# define GC_CALLOC_EXPLICTLY_TYPED(n, bytes, d) \ + GC_calloc_explicitly_typed(n, bytes, d) +#endif /* !GC_DEBUG */ + + +#endif /* _GC_TYPED_H */ + diff --git a/gc/include/javaxfc.h b/gc/include/javaxfc.h new file mode 100644 index 0000000..880020c --- /dev/null +++ b/gc/include/javaxfc.h @@ -0,0 +1,41 @@ +# ifndef GC_H +# include "gc.h" +# endif + +/* + * Invoke all remaining finalizers that haven't yet been run. + * This is needed for strict compliance with the Java standard, + * which can make the runtime guarantee that all finalizers are run. + * This is problematic for several reasons: + * 1) It means that finalizers, and all methods calle by them, + * must be prepared to deal with objects that have been finalized in + * spite of the fact that they are still referenced by statically + * allocated pointer variables. + * 1) It may mean that we get stuck in an infinite loop running + * finalizers which create new finalizable objects, though that's + * probably unlikely. + * Thus this is not recommended for general use. + */ +void GC_finalize_all(); + +/* + * A version of GC_register_finalizer that allows the object to be + * finalized before the objects it references. This is again error + * prone, in that it makes it easy to accidentally reference finalized + * objects. Again, recommended only for JVM implementors. + */ +void GC_register_finalizer_no_order(GC_PTR obj, + GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR * ocd); + +void GC_debug_register_finalizer_no_order(GC_PTR obj, + GC_finalization_proc fn, GC_PTR cd, + GC_finalization_proc *ofn, GC_PTR * ocd); + +#ifdef GC_DEBUG +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_debug_register_finalizer_no_order(p, f, d, of, od) +#else +# define GC_REGISTER_FINALIZER(p, f, d, of, od) \ + GC_register_finalizer_no_order(p, f, d, of, od) +#endif diff --git a/gc/include/leak_detector.h b/gc/include/leak_detector.h new file mode 100644 index 0000000..6786825 --- /dev/null +++ b/gc/include/leak_detector.h @@ -0,0 +1,7 @@ +#define GC_DEBUG +#include "gc.h" +#define malloc(n) GC_MALLOC(n) +#define calloc(m,n) GC_MALLOC(m*n) +#define free(p) GC_FREE(p) +#define realloc(p,n) GC_REALLOC(n) +#define CHECK_LEAKS() GC_gcollect() diff --git a/gc/include/new_gc_alloc.h b/gc/include/new_gc_alloc.h new file mode 100644 index 0000000..5771388 --- /dev/null +++ b/gc/include/new_gc_alloc.h @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1996-1998 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +// +// This is a revision of gc_alloc.h for SGI STL versions > 3.0 +// Unlike earlier versions, it supplements the standard "alloc.h" +// instead of replacing it. +// +// This is sloppy about variable names used in header files. +// It also doesn't yet understand the new header file names or +// namespaces. +// +// This assumes the collector has been compiled with -DATOMIC_UNCOLLECTABLE +// and -DALL_INTERIOR_POINTERS. We also recommend +// -DREDIRECT_MALLOC=GC_uncollectable_malloc. +// +// Some of this could be faster in the explicit deallocation case. +// In particular, we spend too much time clearing objects on the +// free lists. That could be avoided. +// +// This uses template classes with static members, and hence does not work +// with g++ 2.7.2 and earlier. +// +// Unlike its predecessor, this one simply defines +// gc_alloc +// single_client_gc_alloc +// traceable_alloc +// single_client_traceable_alloc +// +// It does not redefine alloc. Nor does it change the default allocator, +// though the user may wish to do so. (The argument against changing +// the default allocator is that it may introduce subtle link compatibility +// problems. The argument for changing it is that the usual default +// allocator is usually a very bad choice for a garbage collected environment.) +// + +#ifndef GC_ALLOC_H + +#include "gc.h" +#include <alloc.h> + +#define GC_ALLOC_H + +#include <stddef.h> +#include <string.h> + +// The following need to match collector data structures. +// We can't include gc_priv.h, since that pulls in way too much stuff. +// This should eventually be factored out into another include file. + +extern "C" { + extern void ** const GC_objfreelist_ptr; + extern void ** const GC_aobjfreelist_ptr; + extern void ** const GC_uobjfreelist_ptr; + extern void ** const GC_auobjfreelist_ptr; + + extern void GC_incr_words_allocd(size_t words); + extern void GC_incr_mem_freed(size_t words); + + extern char * GC_generic_malloc_words_small(size_t word, int kind); +} + +// Object kinds; must match PTRFREE, NORMAL, UNCOLLECTABLE, and +// AUNCOLLECTABLE in gc_priv.h. + +enum { GC_PTRFREE = 0, GC_NORMAL = 1, GC_UNCOLLECTABLE = 2, + GC_AUNCOLLECTABLE = 3 }; + +enum { GC_max_fast_bytes = 255 }; + +enum { GC_bytes_per_word = sizeof(char *) }; + +enum { GC_byte_alignment = 8 }; + +enum { GC_word_alignment = GC_byte_alignment/GC_bytes_per_word }; + +inline void * &GC_obj_link(void * p) +{ return *(void **)p; } + +// Compute a number of words >= n+1 bytes. +// The +1 allows for pointers one past the end. +inline size_t GC_round_up(size_t n) +{ + return ((n + GC_byte_alignment)/GC_byte_alignment)*GC_word_alignment; +} + +// The same but don't allow for extra byte. +inline size_t GC_round_up_uncollectable(size_t n) +{ + return ((n + GC_byte_alignment - 1)/GC_byte_alignment)*GC_word_alignment; +} + +template <int dummy> +class GC_aux_template { +public: + // File local count of allocated words. Occasionally this is + // added into the global count. A separate count is necessary since the + // real one must be updated with a procedure call. + static size_t GC_words_recently_allocd; + + // Same for uncollectable mmory. Not yet reflected in either + // GC_words_recently_allocd or GC_non_gc_bytes. + static size_t GC_uncollectable_words_recently_allocd; + + // Similar counter for explicitly deallocated memory. + static size_t GC_mem_recently_freed; + + // Again for uncollectable memory. + static size_t GC_uncollectable_mem_recently_freed; + + static void * GC_out_of_line_malloc(size_t nwords, int kind); +}; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_words_recently_allocd = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_mem_recently_freed = 0; + +template <int dummy> +size_t GC_aux_template<dummy>::GC_uncollectable_mem_recently_freed = 0; + +template <int dummy> +void * GC_aux_template<dummy>::GC_out_of_line_malloc(size_t nwords, int kind) +{ + GC_words_recently_allocd += GC_uncollectable_words_recently_allocd; + GC_non_gc_bytes += + GC_bytes_per_word * GC_uncollectable_words_recently_allocd; + GC_uncollectable_words_recently_allocd = 0; + + GC_mem_recently_freed += GC_uncollectable_mem_recently_freed; + GC_non_gc_bytes -= + GC_bytes_per_word * GC_uncollectable_mem_recently_freed; + GC_uncollectable_mem_recently_freed = 0; + + GC_incr_words_allocd(GC_words_recently_allocd); + GC_words_recently_allocd = 0; + + GC_incr_mem_freed(GC_mem_recently_freed); + GC_mem_recently_freed = 0; + + return GC_generic_malloc_words_small(nwords, kind); +} + +typedef GC_aux_template<0> GC_aux; + +// A fast, single-threaded, garbage-collected allocator +// We assume the first word will be immediately overwritten. +// In this version, deallocation is not a noop, and explicit +// deallocation is likely to help performance. +template <int dummy> +class single_client_gc_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc(n); + flh = GC_objfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_NORMAL); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic(n); + flh = GC_aobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_PTRFREE); + } + *flh = GC_obj_link(op); + GC_aux::GC_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_objfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + memset((char *)p + GC_bytes_per_word, 0, + GC_bytes_per_word * (nwords - 1)); + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_aobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_gc_alloc_template<0> single_client_gc_alloc; + +// Once more, for uncollectable objects. +template <int dummy> +class single_client_traceable_alloc_template { + public: + static void * allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_uncollectable(n); + flh = GC_uobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_UNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void * ptr_free_allocate(size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + void * op; + + if (n > GC_max_fast_bytes) return GC_malloc_atomic_uncollectable(n); + flh = GC_auobjfreelist_ptr + nwords; + if (0 == (op = *flh)) { + return GC_aux::GC_out_of_line_malloc(nwords, GC_AUNCOLLECTABLE); + } + *flh = GC_obj_link(op); + GC_aux::GC_uncollectable_words_recently_allocd += nwords; + return op; + } + static void deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_uobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } + static void ptr_free_deallocate(void *p, size_t n) + { + size_t nwords = GC_round_up_uncollectable(n); + void ** flh; + + if (n > GC_max_fast_bytes) { + GC_free(p); + } else { + flh = GC_auobjfreelist_ptr + nwords; + GC_obj_link(p) = *flh; + *flh = p; + GC_aux::GC_uncollectable_mem_recently_freed += nwords; + } + } +}; + +typedef single_client_traceable_alloc_template<0> single_client_traceable_alloc; + +template < int dummy > +class gc_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic(n); } + static void deallocate(void *, size_t) { } + static void ptr_free_deallocate(void *, size_t) { } +}; + +typedef gc_alloc_template < 0 > gc_alloc; + +template < int dummy > +class traceable_alloc_template { + public: + static void * allocate(size_t n) { return GC_malloc_uncollectable(n); } + static void * ptr_free_allocate(size_t n) + { return GC_malloc_atomic_uncollectable(n); } + static void deallocate(void *p, size_t) { GC_free(p); } + static void ptr_free_deallocate(void *p, size_t) { GC_free(p); } +}; + +typedef traceable_alloc_template < 0 > traceable_alloc; + +#ifdef _SGI_SOURCE + +// We want to specialize simple_alloc so that it does the right thing +// for all pointerfree types. At the moment there is no portable way to +// even approximate that. The following approximation should work for +// SGI compilers, and perhaps some others. + +# define __GC_SPECIALIZE(T,alloc) \ +class simple_alloc<T, alloc> { \ +public: \ + static T *allocate(size_t n) \ + { return 0 == n? 0 : \ + (T*) alloc::ptr_free_allocate(n * sizeof (T)); } \ + static T *allocate(void) \ + { return (T*) alloc::ptr_free_allocate(sizeof (T)); } \ + static void deallocate(T *p, size_t n) \ + { if (0 != n) alloc::ptr_free_deallocate(p, n * sizeof (T)); } \ + static void deallocate(T *p) \ + { alloc::ptr_free_deallocate(p, sizeof (T)); } \ +}; + +__GC_SPECIALIZE(char, gc_alloc) +__GC_SPECIALIZE(int, gc_alloc) +__GC_SPECIALIZE(unsigned, gc_alloc) +__GC_SPECIALIZE(float, gc_alloc) +__GC_SPECIALIZE(double, gc_alloc) + +__GC_SPECIALIZE(char, traceable_alloc) +__GC_SPECIALIZE(int, traceable_alloc) +__GC_SPECIALIZE(unsigned, traceable_alloc) +__GC_SPECIALIZE(float, traceable_alloc) +__GC_SPECIALIZE(double, traceable_alloc) + +__GC_SPECIALIZE(char, single_client_gc_alloc) +__GC_SPECIALIZE(int, single_client_gc_alloc) +__GC_SPECIALIZE(unsigned, single_client_gc_alloc) +__GC_SPECIALIZE(float, single_client_gc_alloc) +__GC_SPECIALIZE(double, single_client_gc_alloc) + +__GC_SPECIALIZE(char, single_client_traceable_alloc) +__GC_SPECIALIZE(int, single_client_traceable_alloc) +__GC_SPECIALIZE(unsigned, single_client_traceable_alloc) +__GC_SPECIALIZE(float, single_client_traceable_alloc) +__GC_SPECIALIZE(double, single_client_traceable_alloc) + +#ifdef __STL_USE_STD_ALLOCATORS + +__STL_BEGIN_NAMESPACE + +template <class _T> +struct _Alloc_traits<_T, gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, gc_alloc > _Alloc_type; + typedef __allocator<_T, gc_alloc > allocator_type; +}; + +inline bool operator==(const gc_alloc&, + const gc_alloc&) +{ + return true; +} + +inline bool operator!=(const gc_alloc&, + const gc_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, single_client_gc_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, single_client_gc_alloc > _Alloc_type; + typedef __allocator<_T, single_client_gc_alloc > allocator_type; +}; + +inline bool operator==(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_gc_alloc&, + const single_client_gc_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, traceable_alloc > _Alloc_type; + typedef __allocator<_T, traceable_alloc > allocator_type; +}; + +inline bool operator==(const traceable_alloc&, + const traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const traceable_alloc&, + const traceable_alloc&) +{ + return false; +} + +template <class _T> +struct _Alloc_traits<_T, single_client_traceable_alloc > +{ + static const bool _S_instanceless = true; + typedef simple_alloc<_T, single_client_traceable_alloc > _Alloc_type; + typedef __allocator<_T, single_client_traceable_alloc > allocator_type; +}; + +inline bool operator==(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return true; +} + +inline bool operator!=(const single_client_traceable_alloc&, + const single_client_traceable_alloc&) +{ + return false; +} + +__STL_END_NAMESPACE + +#endif /* __STL_USE_STD_ALLOCATORS */ + +#endif /* _SGI_SOURCE */ + +#endif /* GC_ALLOC_H */ diff --git a/gc/include/private/cord_pos.h b/gc/include/private/cord_pos.h new file mode 100644 index 0000000..d2b24bb --- /dev/null +++ b/gc/include/private/cord_pos.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:23 pm PDT */ +# ifndef CORD_POSITION_H + +/* The representation of CORD_position. This is private to the */ +/* implementation, but the size is known to clients. Also */ +/* the implementation of some exported macros relies on it. */ +/* Don't use anything defined here and not in cord.h. */ + +# define MAX_DEPTH 48 + /* The maximum depth of a balanced cord + 1. */ + /* We don't let cords get deeper than MAX_DEPTH. */ + +struct CORD_pe { + CORD pe_cord; + size_t pe_start_pos; +}; + +/* A structure describing an entry on the path from the root */ +/* to current position. */ +typedef struct CORD_Pos { + size_t cur_pos; + int path_len; +# define CORD_POS_INVALID (0x55555555) + /* path_len == INVALID <==> position invalid */ + const char *cur_leaf; /* Current leaf, if it is a string. */ + /* If the current leaf is a function, */ + /* then this may point to function_buf */ + /* containing the next few characters. */ + /* Always points to a valid string */ + /* containing the current character */ + /* unless cur_end is 0. */ + size_t cur_start; /* Start position of cur_leaf */ + size_t cur_end; /* Ending position of cur_leaf */ + /* 0 if cur_leaf is invalid. */ + struct CORD_pe path[MAX_DEPTH + 1]; + /* path[path_len] is the leaf corresponding to cur_pos */ + /* path[0].pe_cord is the cord we point to. */ +# define FUNCTION_BUF_SZ 8 + char function_buf[FUNCTION_BUF_SZ]; /* Space for next few chars */ + /* from function node. */ +} CORD_pos[1]; + +/* Extract the cord from a position: */ +CORD CORD_pos_to_cord(CORD_pos p); + +/* Extract the current index from a position: */ +size_t CORD_pos_to_index(CORD_pos p); + +/* Fetch the character located at the given position: */ +char CORD_pos_fetch(CORD_pos p); + +/* Initialize the position to refer to the give cord and index. */ +/* Note that this is the most expensive function on positions: */ +void CORD_set_pos(CORD_pos p, CORD x, size_t i); + +/* Advance the position to the next character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past end: */ +void CORD_next(CORD_pos p); + +/* Move the position to the preceding character. */ +/* P must be initialized and valid. */ +/* Invalidates p if past beginning: */ +void CORD_prev(CORD_pos p); + +/* Is the position valid, i.e. inside the cord? */ +int CORD_pos_valid(CORD_pos p); + +char CORD__pos_fetch(CORD_pos); +void CORD__next(CORD_pos); +void CORD__prev(CORD_pos); + +#define CORD_pos_fetch(p) \ + (((p)[0].cur_end != 0)? \ + (p)[0].cur_leaf[(p)[0].cur_pos - (p)[0].cur_start] \ + : CORD__pos_fetch(p)) + +#define CORD_next(p) \ + (((p)[0].cur_pos + 1 < (p)[0].cur_end)? \ + (p)[0].cur_pos++ \ + : (CORD__next(p), 0)) + +#define CORD_prev(p) \ + (((p)[0].cur_end != 0 && (p)[0].cur_pos > (p)[0].cur_start)? \ + (p)[0].cur_pos-- \ + : (CORD__prev(p), 0)) + +#define CORD_pos_to_index(p) ((p)[0].cur_pos) + +#define CORD_pos_to_cord(p) ((p)[0].path[0].pe_cord) + +#define CORD_pos_valid(p) ((p)[0].path_len != CORD_POS_INVALID) + +/* Some grubby stuff for performance-critical friends: */ +#define CORD_pos_chars_left(p) ((long)((p)[0].cur_end) - (long)((p)[0].cur_pos)) + /* Number of characters in cache. <= 0 ==> none */ + +#define CORD_pos_advance(p,n) ((p)[0].cur_pos += (n) - 1, CORD_next(p)) + /* Advance position by n characters */ + /* 0 < n < CORD_pos_chars_left(p) */ + +#define CORD_pos_cur_char_addr(p) \ + (p)[0].cur_leaf + ((p)[0].cur_pos - (p)[0].cur_start) + /* address of current character in cache. */ + +#endif diff --git a/gc/include/private/gc_hdrs.h b/gc/include/private/gc_hdrs.h new file mode 100644 index 0000000..60dc2ad --- /dev/null +++ b/gc/include/private/gc_hdrs.h @@ -0,0 +1,135 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, July 11, 1995 11:54 am PDT */ +# ifndef GC_HEADERS_H +# define GC_HEADERS_H +typedef struct hblkhdr hdr; + +# if CPP_WORDSZ != 32 && CPP_WORDSZ < 36 + --> Get a real machine. +# endif + +/* + * The 2 level tree data structure that is used to find block headers. + * If there are more than 32 bits in a pointer, the top level is a hash + * table. + */ + +# if CPP_WORDSZ > 32 +# define HASH_TL +# endif + +/* Define appropriate out-degrees for each of the two tree levels */ +# ifdef SMALL_CONFIG +# define LOG_BOTTOM_SZ 11 + /* Keep top index size reasonable with smaller blocks. */ +# else +# define LOG_BOTTOM_SZ 10 +# endif +# ifndef HASH_TL +# define LOG_TOP_SZ (WORDSZ - LOG_BOTTOM_SZ - LOG_HBLKSIZE) +# else +# define LOG_TOP_SZ 11 +# endif +# define TOP_SZ (1 << LOG_TOP_SZ) +# define BOTTOM_SZ (1 << LOG_BOTTOM_SZ) + +typedef struct bi { + hdr * index[BOTTOM_SZ]; + /* + * The bottom level index contains one of three kinds of values: + * 0 means we're not responsible for this block, + * or this is a block other than the first one in a free block. + * 1 < (long)X <= MAX_JUMP means the block starts at least + * X * HBLKSIZE bytes before the current address. + * A valid pointer points to a hdr structure. (The above can't be + * valid pointers due to the GET_MEM return convention.) + */ + struct bi * asc_link; /* All indices are linked in */ + /* ascending order... */ + struct bi * desc_link; /* ... and in descending order. */ + word key; /* high order address bits. */ +# ifdef HASH_TL + struct bi * hash_link; /* Hash chain link. */ +# endif +} bottom_index; + +/* extern bottom_index GC_all_nils; - really part of GC_arrays */ + +/* extern bottom_index * GC_top_index []; - really part of GC_arrays */ + /* Each entry points to a bottom_index. */ + /* On a 32 bit machine, it points to */ + /* the index for a set of high order */ + /* bits equal to the index. For longer */ + /* addresses, we hash the high order */ + /* bits to compute the index in */ + /* GC_top_index, and each entry points */ + /* to a hash chain. */ + /* The last entry in each chain is */ + /* GC_all_nils. */ + + +# define MAX_JUMP (HBLKSIZE - 1) + +# define HDR_FROM_BI(bi, p) \ + ((bi)->index[((word)(p) >> LOG_HBLKSIZE) & (BOTTOM_SZ - 1)]) +# ifndef HASH_TL +# define BI(p) (GC_top_index \ + [(word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE)]) +# define HDR_INNER(p) HDR_FROM_BI(BI(p),p) +# ifdef SMALL_CONFIG +# define HDR(p) GC_find_header((ptr_t)(p)) +# else +# define HDR(p) HDR_INNER(p) +# endif +# define GET_BI(p, bottom_indx) (bottom_indx) = BI(p) +# define GET_HDR(p, hhdr) (hhdr) = HDR(p) +# define SET_HDR(p, hhdr) HDR_INNER(p) = (hhdr) +# define GET_HDR_ADDR(p, ha) (ha) = &(HDR_INNER(p)) +# else /* hash */ +/* Hash function for tree top level */ +# define TL_HASH(hi) ((hi) & (TOP_SZ - 1)) +/* Set bottom_indx to point to the bottom index for address p */ +# define GET_BI(p, bottom_indx) \ + { \ + register word hi = \ + (word)(p) >> (LOG_BOTTOM_SZ + LOG_HBLKSIZE); \ + register bottom_index * _bi = GC_top_index[TL_HASH(hi)]; \ + \ + while (_bi -> key != hi && _bi != GC_all_nils) \ + _bi = _bi -> hash_link; \ + (bottom_indx) = _bi; \ + } +# define GET_HDR_ADDR(p, ha) \ + { \ + register bottom_index * bi; \ + \ + GET_BI(p, bi); \ + (ha) = &(HDR_FROM_BI(bi, p)); \ + } +# define GET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + (hhdr) = *_ha; } +# define SET_HDR(p, hhdr) { register hdr ** _ha; GET_HDR_ADDR(p, _ha); \ + *_ha = (hhdr); } +# define HDR(p) GC_find_header((ptr_t)(p)) +# endif + +/* Is the result a forwarding address to someplace closer to the */ +/* beginning of the block or NIL? */ +# define IS_FORWARDING_ADDR_OR_NIL(hhdr) ((unsigned long) (hhdr) <= MAX_JUMP) + +/* Get an HBLKSIZE aligned address closer to the beginning of the block */ +/* h. Assumes hhdr == HDR(h) and IS_FORWARDING_ADDR(hhdr). */ +# define FORWARDED_ADDR(h, hhdr) ((struct hblk *)(h) - (unsigned long)(hhdr)) +# endif /* GC_HEADERS_H */ diff --git a/gc/include/private/gc_priv.h b/gc/include/private/gc_priv.h new file mode 100644 index 0000000..5ce52a7 --- /dev/null +++ b/gc/include/private/gc_priv.h @@ -0,0 +1,1748 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 16, 1996 2:30 pm PST */ + + +# ifndef GC_PRIVATE_H +# define GC_PRIVATE_H + +#if defined(mips) && defined(SYSTYPE_BSD) && defined(sony_news) + /* sony RISC NEWS, NEWSOS 4 */ +# define BSD_TIME +/* typedef long ptrdiff_t; -- necessary on some really old systems */ +#endif + +#if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# define BSD_TIME +#endif + +#ifdef BSD_TIME +# include <sys/types.h> +# include <sys/time.h> +# include <sys/resource.h> +#endif /* BSD_TIME */ + +# ifndef GC_H +# include "gc.h" +# endif + +typedef GC_word word; +typedef GC_signed_word signed_word; + +# ifndef CONFIG_H +# include "gcconfig.h" +# endif + +# ifndef HEADERS_H +# include "gc_hdrs.h" +# endif + +typedef int GC_bool; +# define TRUE 1 +# define FALSE 0 + +typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements. */ + /* Preferably identical to caddr_t, if it */ + /* exists. */ + +#if defined(__STDC__) +# include <stdlib.h> +# if !(defined( sony_news ) ) +# include <stddef.h> +# endif +# define VOLATILE volatile +# define CONST const +#else +# ifdef MSWIN32 +# include <stdlib.h> +# endif +# define VOLATILE +# define CONST +#endif + +#if 0 /* was once defined for AMIGA */ +# define GC_FAR __far +#else +# define GC_FAR +#endif + +/*********************************/ +/* */ +/* Definitions for conservative */ +/* collector */ +/* */ +/*********************************/ + +/*********************************/ +/* */ +/* Easily changeable parameters */ +/* */ +/*********************************/ + +#define STUBBORN_ALLOC /* Define stubborn allocation primitives */ +#if defined(SRC_M3) || defined(SMALL_CONFIG) +# undef STUBBORN_ALLOC +#endif + + +/* #define ALL_INTERIOR_POINTERS */ + /* Forces all pointers into the interior of an */ + /* object to be considered valid. Also causes the */ + /* sizes of all objects to be inflated by at least */ + /* one byte. This should suffice to guarantee */ + /* that in the presence of a compiler that does */ + /* not perform garbage-collector-unsafe */ + /* optimizations, all portable, strictly ANSI */ + /* conforming C programs should be safely usable */ + /* with malloc replaced by GC_malloc and free */ + /* calls removed. There are several disadvantages: */ + /* 1. There are probably no interesting, portable, */ + /* strictly ANSI conforming C programs. */ + /* 2. This option makes it hard for the collector */ + /* to allocate space that is not ``pointed to'' */ + /* by integers, etc. Under SunOS 4.X with a */ + /* statically linked libc, we empiricaly */ + /* observed that it would be difficult to */ + /* allocate individual objects larger than 100K. */ + /* Even if only smaller objects are allocated, */ + /* more swap space is likely to be needed. */ + /* Fortunately, much of this will never be */ + /* touched. */ + /* If you can easily avoid using this option, do. */ + /* If not, try to keep individual objects small. */ + +#define PRINTSTATS /* Print garbage collection statistics */ + /* For less verbose output, undefine in reclaim.c */ + +#define PRINTTIMES /* Print the amount of time consumed by each garbage */ + /* collection. */ + +#define PRINTBLOCKS /* Print object sizes associated with heap blocks, */ + /* whether the objects are atomic or composite, and */ + /* whether or not the block was found to be empty */ + /* during the reclaim phase. Typically generates */ + /* about one screenful per garbage collection. */ +#undef PRINTBLOCKS + +#ifdef SILENT +# ifdef PRINTSTATS +# undef PRINTSTATS +# endif +# ifdef PRINTTIMES +# undef PRINTTIMES +# endif +# ifdef PRINTNBLOCKS +# undef PRINTNBLOCKS +# endif +#endif + +#if defined(PRINTSTATS) && !defined(GATHERSTATS) +# define GATHERSTATS +#endif + +#ifdef FINALIZE_ON_DEMAND +# define GC_INVOKE_FINALIZERS() +#else +# define GC_INVOKE_FINALIZERS() (void)GC_invoke_finalizers() +#endif + +#define MERGE_SIZES /* Round up some object sizes, so that fewer distinct */ + /* free lists are actually maintained. This applies */ + /* only to the top level routines in misc.c, not to */ + /* user generated code that calls GC_allocobj and */ + /* GC_allocaobj directly. */ + /* Slows down average programs slightly. May however */ + /* substantially reduce fragmentation if allocation */ + /* request sizes are widely scattered. */ + /* May save significant amounts of space for obj_map */ + /* entries. */ + +#ifndef OLD_BLOCK_ALLOC + /* Macros controlling large block allocation strategy. */ +# define EXACT_FIRST /* Make a complete pass through the large object */ + /* free list before splitting a block */ +# define PRESERVE_LAST /* Do not divide last allocated heap segment */ + /* unless we would otherwise need to expand the */ + /* heap. */ +#endif + +/* ALIGN_DOUBLE requires MERGE_SIZES at present. */ +# if defined(ALIGN_DOUBLE) && !defined(MERGE_SIZES) +# define MERGE_SIZES +# endif + +#if defined(ALL_INTERIOR_POINTERS) && !defined(DONT_ADD_BYTE_AT_END) +# define ADD_BYTE_AT_END +#endif + + +# ifndef LARGE_CONFIG +# define MINHINCR 16 /* Minimum heap increment, in blocks of HBLKSIZE */ + /* Must be multiple of largest page size. */ +# define MAXHINCR 512 /* Maximum heap increment, in blocks */ +# else +# define MINHINCR 64 +# define MAXHINCR 4096 +# endif + +# define TIME_LIMIT 50 /* We try to keep pause times from exceeding */ + /* this by much. In milliseconds. */ + +# define BL_LIMIT GC_black_list_spacing + /* If we need a block of N bytes, and we have */ + /* a block of N + BL_LIMIT bytes available, */ + /* and N > BL_LIMIT, */ + /* but all possible positions in it are */ + /* blacklisted, we just use it anyway (and */ + /* print a warning, if warnings are enabled). */ + /* This risks subsequently leaking the block */ + /* due to a false reference. But not using */ + /* the block risks unreasonable immediate */ + /* heap growth. */ + +/*********************************/ +/* */ +/* Stack saving for debugging */ +/* */ +/*********************************/ + +#ifdef SAVE_CALL_CHAIN + +/* + * Number of frames and arguments to save in objects allocated by + * debugging allocator. + */ +# define NFRAMES 6 /* Number of frames to save. Even for */ + /* alignment reasons. */ +# define NARGS 2 /* Mumber of arguments to save for each call. */ + +# define NEED_CALLINFO + +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ +void GC_save_callers (/* struct callinfo info[NFRAMES] */); + +void GC_print_callers (/* struct callinfo info[NFRAMES] */); + +#else + +# ifdef GC_ADD_CALLER +# define NFRAMES 1 +# define NARGS 0 +# define NEED_CALLINFO +# endif + +#endif + +#ifdef NEED_CALLINFO + struct callinfo { + word ci_pc; +# if NARGS > 0 + word ci_arg[NARGS]; /* bit-wise complement to avoid retention */ +# endif +# if defined(ALIGN_DOUBLE) && (NFRAMES * (NARGS + 1)) % 2 == 1 + /* Likely alignment problem. */ + word ci_dummy; +# endif + }; +#endif + + +/*********************************/ +/* */ +/* OS interface routines */ +/* */ +/*********************************/ + +#ifdef BSD_TIME +# undef CLOCK_TYPE +# undef GET_TIME +# undef MS_TIME_DIFF +# define CLOCK_TYPE struct timeval +# define GET_TIME(x) { struct rusage rusage; \ + getrusage (RUSAGE_SELF, &rusage); \ + x = rusage.ru_utime; } +# define MS_TIME_DIFF(a,b) ((double) (a.tv_sec - b.tv_sec) * 1000.0 \ + + (double) (a.tv_usec - b.tv_usec) / 1000.0) +#else /* !BSD_TIME */ +# include <time.h> +# if !defined(__STDC__) && defined(SPARC) && defined(SUNOS4) + clock_t clock(); /* Not in time.h, where it belongs */ +# endif +# if defined(FREEBSD) && !defined(CLOCKS_PER_SEC) +# include <machine/limits.h> +# define CLOCKS_PER_SEC CLK_TCK +# endif +# if !defined(CLOCKS_PER_SEC) +# define CLOCKS_PER_SEC 1000000 +/* + * This is technically a bug in the implementation. ANSI requires that + * CLOCKS_PER_SEC be defined. But at least under SunOS4.1.1, it isn't. + * Also note that the combination of ANSI C and POSIX is incredibly gross + * here. The type clock_t is used by both clock() and times(). But on + * some machines these use different notions of a clock tick, CLOCKS_PER_SEC + * seems to apply only to clock. Hence we use it here. On many machines, + * including SunOS, clock actually uses units of microseconds (which are + * not really clock ticks). + */ +# endif +# define CLOCK_TYPE clock_t +# define GET_TIME(x) x = clock() +# define MS_TIME_DIFF(a,b) ((unsigned long) \ + (1000.0*(double)((a)-(b))/(double)CLOCKS_PER_SEC)) +#endif /* !BSD_TIME */ + +/* We use bzero and bcopy internally. They may not be available. */ +# if defined(SPARC) && defined(SUNOS4) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(AMIGA) +# define BCOPY_EXISTS +# endif +# if defined(M68K) && defined(NEXT) +# define BCOPY_EXISTS +# endif +# if defined(VAX) +# define BCOPY_EXISTS +# endif +# if defined(AMIGA) +# include <string.h> +# define BCOPY_EXISTS +# endif + +# ifndef BCOPY_EXISTS +# include <string.h> +# define BCOPY(x,y,n) memcpy(y, x, (size_t)(n)) +# define BZERO(x,n) memset(x, 0, (size_t)(n)) +# else +# define BCOPY(x,y,n) bcopy((char *)(x),(char *)(y),(int)(n)) +# define BZERO(x,n) bzero((char *)(x),(int)(n)) +# endif + +/* HBLKSIZE aligned allocation. 0 is taken to mean failure */ +/* space is assumed to be cleared. */ +/* In the case os USE_MMAP, the argument must also be a */ +/* physical page size. */ +/* GET_MEM is currently not assumed to retrieve 0 filled space, */ +/* though we should perhaps take advantage of the case in which */ +/* does. */ +# ifdef PCR + char * real_malloc(); +# define GET_MEM(bytes) HBLKPTR(real_malloc((size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef OS2 + void * os2_alloc(size_t bytes); +# define GET_MEM(bytes) HBLKPTR((ptr_t)os2_alloc((size_t)bytes \ + + GC_page_size) \ + + GC_page_size-1) +# else +# if defined(AMIGA) || defined(NEXT) || defined(MACOSX) || defined(DOS4GW) +# define GET_MEM(bytes) HBLKPTR((size_t) \ + calloc(1, (size_t)bytes + GC_page_size) \ + + GC_page_size-1) +# else +# ifdef MSWIN32 + extern ptr_t GC_win32_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_win32_get_mem(bytes) +# else +# ifdef MACOS +# if defined(USE_TEMPORARY_MEMORY) + extern Ptr GC_MacTemporaryNewPtr(size_t size, + Boolean clearMemory); +# define GET_MEM(bytes) HBLKPTR( \ + GC_MacTemporaryNewPtr(bytes + GC_page_size, true) \ + + GC_page_size-1) +# else +# define GET_MEM(bytes) HBLKPTR( \ + NewPtrClear(bytes + GC_page_size) + GC_page_size-1) +# endif +# else + extern ptr_t GC_unix_get_mem(); +# define GET_MEM(bytes) (struct hblk *)GC_unix_get_mem(bytes) +# endif +# endif +# endif +# endif +# endif + +/* + * Mutual exclusion between allocator/collector routines. + * Needed if there is more than one allocator thread. + * FASTLOCK() is assumed to try to acquire the lock in a cheap and + * dirty way that is acceptable for a few instructions, e.g. by + * inhibiting preemption. This is assumed to have succeeded only + * if a subsequent call to FASTLOCK_SUCCEEDED() returns TRUE. + * FASTUNLOCK() is called whether or not FASTLOCK_SUCCEEDED(). + * If signals cannot be tolerated with the FASTLOCK held, then + * FASTLOCK should disable signals. The code executed under + * FASTLOCK is otherwise immune to interruption, provided it is + * not restarted. + * DCL_LOCK_STATE declares any local variables needed by LOCK and UNLOCK + * and/or DISABLE_SIGNALS and ENABLE_SIGNALS and/or FASTLOCK. + * (There is currently no equivalent for FASTLOCK.) + */ +# ifdef THREADS +# ifdef PCR_OBSOLETE /* Faster, but broken with multiple lwp's */ +# include "th/PCR_Th.h" +# include "th/PCR_ThCrSec.h" + extern struct PCR_Th_MLRep GC_allocate_ml; +# define DCL_LOCK_STATE PCR_sigset_t GC_old_sig_mask +# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# define FASTLOCK() PCR_ThCrSec_EnterSys() + /* Here we cheat (a lot): */ +# define FASTLOCK_SUCCEEDED() (*(int *)(&GC_allocate_ml) == 0) + /* TRUE if nobody currently holds the lock */ +# define FASTUNLOCK() PCR_ThCrSec_ExitSys() +# endif +# ifdef PCR +# include <base/PCR_Base.h> +# include <th/PCR_Th.h> + extern PCR_Th_ML GC_allocate_ml; +# define DCL_LOCK_STATE \ + PCR_ERes GC_fastLockRes; PCR_sigset_t GC_old_sig_mask +# define LOCK() PCR_Th_ML_Acquire(&GC_allocate_ml) +# define UNLOCK() PCR_Th_ML_Release(&GC_allocate_ml) +# define FASTLOCK() (GC_fastLockRes = PCR_Th_ML_Try(&GC_allocate_ml)) +# define FASTLOCK_SUCCEEDED() (GC_fastLockRes == PCR_ERes_okay) +# define FASTUNLOCK() {\ + if( FASTLOCK_SUCCEEDED() ) PCR_Th_ML_Release(&GC_allocate_ml); } +# endif +# ifdef SRC_M3 + extern word RT0u__inCritical; +# define LOCK() RT0u__inCritical++ +# define UNLOCK() RT0u__inCritical-- +# endif +# ifdef SOLARIS_THREADS +# include <thread.h> +# include <signal.h> + extern mutex_t GC_allocate_ml; +# define LOCK() mutex_lock(&GC_allocate_ml); +# define UNLOCK() mutex_unlock(&GC_allocate_ml); +# endif +# ifdef LINUX_THREADS +# include <pthread.h> +# ifdef __i386__ + inline static int GC_test_and_set(volatile unsigned int *addr) { + int oldval; + /* Note: the "xchg" instruction does not need a "lock" prefix */ + __asm__ __volatile__("xchgl %0, %1" + : "=r"(oldval), "=m"(*(addr)) + : "0"(1), "m"(*(addr))); + return oldval; + } +# else + -- > Need implementation of GC_test_and_set() +# endif +# define GC_clear(addr) (*(addr) = 0) + + extern volatile unsigned int GC_allocate_lock; + /* This is not a mutex because mutexes that obey the (optional) */ + /* POSIX scheduling rules are subject to convoys in high contention */ + /* applications. This is basically a spin lock. */ + extern pthread_t GC_lock_holder; + extern void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +# define NO_THREAD (pthread_t)(-1) +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +# ifdef UNDEFINED +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# define LOCK() \ + { if (GC_test_and_set(&GC_allocate_lock)) GC_lock(); } +# define UNLOCK() \ + GC_clear(&GC_allocate_lock) +# endif + extern GC_bool GC_collecting; +# define ENTER_GC() \ + { \ + GC_collecting = 1; \ + } +# define EXIT_GC() GC_collecting = 0; +# endif /* LINUX_THREADS */ +# if defined(IRIX_THREADS) || defined(IRIX_JDK_THREADS) +# include <pthread.h> +# include <mutex.h> + +# if __mips < 3 || !(defined (_ABIN32) || defined(_ABI64)) \ + || !defined(_COMPILER_VERSION) || _COMPILER_VERSION < 700 +# define GC_test_and_set(addr, v) test_and_set(addr,v) +# else +# define GC_test_and_set(addr, v) __test_and_set(addr,v) +# endif + extern unsigned long GC_allocate_lock; + /* This is not a mutex because mutexes that obey the (optional) */ + /* POSIX scheduling rules are subject to convoys in high contention */ + /* applications. This is basically a spin lock. */ + extern pthread_t GC_lock_holder; + extern void GC_lock(void); + /* Allocation lock holder. Only set if acquired by client through */ + /* GC_call_with_alloc_lock. */ +# define SET_LOCK_HOLDER() GC_lock_holder = pthread_self() +# define NO_THREAD (pthread_t)(-1) +# define UNSET_LOCK_HOLDER() GC_lock_holder = NO_THREAD +# define I_HOLD_LOCK() (pthread_equal(GC_lock_holder, pthread_self())) +# ifdef UNDEFINED +# define LOCK() pthread_mutex_lock(&GC_allocate_ml) +# define UNLOCK() pthread_mutex_unlock(&GC_allocate_ml) +# else +# define LOCK() { if (GC_test_and_set(&GC_allocate_lock, 1)) GC_lock(); } +# if __mips >= 3 && (defined (_ABIN32) || defined(_ABI64)) \ + && defined(_COMPILER_VERSION) && _COMPILER_VERSION >= 700 +# define UNLOCK() __lock_release(&GC_allocate_lock) +# else + /* The function call in the following should prevent the */ + /* compiler from moving assignments to below the UNLOCK. */ + /* This is probably not necessary for ucode or gcc 2.8. */ + /* It may be necessary for Ragnarok and future gcc */ + /* versions. */ +# define UNLOCK() { GC_noop1(&GC_allocate_lock); \ + *(volatile unsigned long *)(&GC_allocate_lock) = 0; } +# endif +# endif + extern GC_bool GC_collecting; +# define ENTER_GC() \ + { \ + GC_collecting = 1; \ + } +# define EXIT_GC() GC_collecting = 0; +# endif /* IRIX_THREADS || IRIX_JDK_THREADS */ +# ifdef WIN32_THREADS +# include <windows.h> + GC_API CRITICAL_SECTION GC_allocate_ml; +# define LOCK() EnterCriticalSection(&GC_allocate_ml); +# define UNLOCK() LeaveCriticalSection(&GC_allocate_ml); +# endif +# ifndef SET_LOCK_HOLDER +# define SET_LOCK_HOLDER() +# define UNSET_LOCK_HOLDER() +# define I_HOLD_LOCK() FALSE + /* Used on platforms were locks can be reacquired, */ + /* so it doesn't matter if we lie. */ +# endif +# else +# define LOCK() +# define UNLOCK() +# endif +# ifndef SET_LOCK_HOLDER +# define SET_LOCK_HOLDER() +# define UNSET_LOCK_HOLDER() +# define I_HOLD_LOCK() FALSE + /* Used on platforms were locks can be reacquired, */ + /* so it doesn't matter if we lie. */ +# endif +# ifndef ENTER_GC +# define ENTER_GC() +# define EXIT_GC() +# endif + +# ifndef DCL_LOCK_STATE +# define DCL_LOCK_STATE +# endif +# ifndef FASTLOCK +# define FASTLOCK() LOCK() +# define FASTLOCK_SUCCEEDED() TRUE +# define FASTUNLOCK() UNLOCK() +# endif + +/* Delay any interrupts or signals that may abort this thread. Data */ +/* structures are in a consistent state outside this pair of calls. */ +/* ANSI C allows both to be empty (though the standard isn't very */ +/* clear on that point). Standard malloc implementations are usually */ +/* neither interruptable nor thread-safe, and thus correspond to */ +/* empty definitions. */ +# ifdef PCR +# define DISABLE_SIGNALS() \ + PCR_Th_SetSigMask(PCR_allSigsBlocked,&GC_old_sig_mask) +# define ENABLE_SIGNALS() \ + PCR_Th_SetSigMask(&GC_old_sig_mask, NIL) +# else +# if defined(SRC_M3) || defined(AMIGA) || defined(SOLARIS_THREADS) \ + || defined(MSWIN32) || defined(MACOS) || defined(DJGPP) \ + || defined(NO_SIGNALS) || defined(IRIX_THREADS) \ + || defined(IRIX_JDK_THREADS) || defined(LINUX_THREADS) + /* Also useful for debugging. */ + /* Should probably use thr_sigsetmask for SOLARIS_THREADS. */ +# define DISABLE_SIGNALS() +# define ENABLE_SIGNALS() +# else +# define DISABLE_SIGNALS() GC_disable_signals() + void GC_disable_signals(); +# define ENABLE_SIGNALS() GC_enable_signals() + void GC_enable_signals(); +# endif +# endif + +/* + * Stop and restart mutator threads. + */ +# ifdef PCR +# include "th/PCR_ThCtl.h" +# define STOP_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal, \ + PCR_allSigsBlocked, \ + PCR_waitForever) +# define START_WORLD() \ + PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null, \ + PCR_allSigsBlocked, \ + PCR_waitForever); +# else +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) + void GC_stop_world(); + void GC_start_world(); +# define STOP_WORLD() GC_stop_world() +# define START_WORLD() GC_start_world() +# else +# define STOP_WORLD() +# define START_WORLD() +# endif +# endif + +/* Abandon ship */ +# ifdef PCR +# define ABORT(s) PCR_Base_Panic(s) +# else +# ifdef SMALL_CONFIG +# define ABORT(msg) abort(); +# else + GC_API void GC_abort(); +# define ABORT(msg) GC_abort(msg); +# endif +# endif + +/* Exit abnormally, but without making a mess (e.g. out of memory) */ +# ifdef PCR +# define EXIT() PCR_Base_Exit(1,PCR_waitForever) +# else +# define EXIT() (void)exit(1) +# endif + +/* Print warning message, e.g. almost out of memory. */ +# define WARN(msg,arg) (*GC_current_warn_proc)(msg, (GC_word)(arg)) +extern GC_warn_proc GC_current_warn_proc; + +/*********************************/ +/* */ +/* Word-size-dependent defines */ +/* */ +/*********************************/ + +#if CPP_WORDSZ == 32 +# define WORDS_TO_BYTES(x) ((x)<<2) +# define BYTES_TO_WORDS(x) ((x)>>2) +# define LOGWL ((word)5) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x1f) /* n mod size of word */ +# if ALIGNMENT != 4 +# define UNALIGNED +# endif +#endif + +#if CPP_WORDSZ == 64 +# define WORDS_TO_BYTES(x) ((x)<<3) +# define BYTES_TO_WORDS(x) ((x)>>3) +# define LOGWL ((word)6) /* log[2] of CPP_WORDSZ */ +# define modWORDSZ(n) ((n) & 0x3f) /* n mod size of word */ +# if ALIGNMENT != 8 +# define UNALIGNED +# endif +#endif + +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1 << (WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define ONES ((word)(-1)) +#define divWORDSZ(n) ((n) >> LOGWL) /* divide n by size of word */ + +/*********************/ +/* */ +/* Size Parameters */ +/* */ +/*********************/ + +/* heap block size, bytes. Should be power of 2 */ + +#ifndef HBLKSIZE +# ifdef SMALL_CONFIG +# define CPP_LOG_HBLKSIZE 10 +# else +# if CPP_WORDSZ == 32 +# define CPP_LOG_HBLKSIZE 12 +# else +# define CPP_LOG_HBLKSIZE 13 +# endif +# endif +#else +# if HBLKSIZE == 512 +# define CPP_LOG_HBLKSIZE 9 +# endif +# if HBLKSIZE == 1024 +# define CPP_LOG_HBLKSIZE 10 +# endif +# if HBLKSIZE == 2048 +# define CPP_LOG_HBLKSIZE 11 +# endif +# if HBLKSIZE == 4096 +# define CPP_LOG_HBLKSIZE 12 +# endif +# if HBLKSIZE == 8192 +# define CPP_LOG_HBLKSIZE 13 +# endif +# if HBLKSIZE == 16384 +# define CPP_LOG_HBLKSIZE 14 +# endif +# ifndef CPP_LOG_HBLKSIZE + --> fix HBLKSIZE +# endif +# undef HBLKSIZE +#endif +# define CPP_HBLKSIZE (1 << CPP_LOG_HBLKSIZE) +# define LOG_HBLKSIZE ((word)CPP_LOG_HBLKSIZE) +# define HBLKSIZE ((word)CPP_HBLKSIZE) + + +/* max size objects supported by freelist (larger objects may be */ +/* allocated, but less efficiently) */ + +#define CPP_MAXOBJSZ BYTES_TO_WORDS(CPP_HBLKSIZE/2) +#define MAXOBJSZ ((word)CPP_MAXOBJSZ) + +# define divHBLKSZ(n) ((n) >> LOG_HBLKSIZE) + +# define HBLK_PTR_DIFF(p,q) divHBLKSZ((ptr_t)p - (ptr_t)q) + /* Equivalent to subtracting 2 hblk pointers. */ + /* We do it this way because a compiler should */ + /* find it hard to use an integer division */ + /* instead of a shift. The bundled SunOS 4.1 */ + /* o.w. sometimes pessimizes the subtraction to */ + /* involve a call to .div. */ + +# define modHBLKSZ(n) ((n) & (HBLKSIZE-1)) + +# define HBLKPTR(objptr) ((struct hblk *)(((word) (objptr)) & ~(HBLKSIZE-1))) + +# define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1)) + +/* Round up byte allocation requests to integral number of words, etc. */ +# ifdef ADD_BYTE_AT_END +# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1)) +# ifdef ALIGN_DOUBLE +# define ALIGNED_WORDS(n) (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2)) & ~1) +# else +# define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) < WORDS_TO_BYTES(MAXOBJSZ)) +# define ADD_SLOP(bytes) ((bytes)+1) +# else +# define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + (WORDS_TO_BYTES(1) - 1)) +# ifdef ALIGN_DOUBLE +# define ALIGNED_WORDS(n) \ + (BYTES_TO_WORDS((n) + WORDS_TO_BYTES(2) - 1) & ~1) +# else +# define ALIGNED_WORDS(n) ROUNDED_UP_WORDS(n) +# endif +# define SMALL_OBJ(bytes) ((bytes) <= WORDS_TO_BYTES(MAXOBJSZ)) +# define ADD_SLOP(bytes) (bytes) +# endif + + +/* + * Hash table representation of sets of pages. This assumes it is + * OK to add spurious entries to sets. + * Used by black-listing code, and perhaps by dirty bit maintenance code. + */ + +# ifdef LARGE_CONFIG +# define LOG_PHT_ENTRIES 17 +# else +# define LOG_PHT_ENTRIES 14 /* Collisions are likely if heap grows */ + /* to more than 16K hblks = 64MB. */ + /* Each hash table occupies 2K bytes. */ +# endif +# define PHT_ENTRIES ((word)1 << LOG_PHT_ENTRIES) +# define PHT_SIZE (PHT_ENTRIES >> LOGWL) +typedef word page_hash_table[PHT_SIZE]; + +# define PHT_HASH(addr) ((((word)(addr)) >> LOG_HBLKSIZE) & (PHT_ENTRIES - 1)) + +# define get_pht_entry_from_index(bl, index) \ + (((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1) +# define set_pht_entry_from_index(bl, index) \ + (bl)[divWORDSZ(index)] |= (word)1 << modWORDSZ(index) +# define clear_pht_entry_from_index(bl, index) \ + (bl)[divWORDSZ(index)] &= ~((word)1 << modWORDSZ(index)) + + + +/********************************************/ +/* */ +/* H e a p B l o c k s */ +/* */ +/********************************************/ + +/* heap block header */ +#define HBLKMASK (HBLKSIZE-1) + +#define BITS_PER_HBLK (HBLKSIZE * 8) + +#define MARK_BITS_PER_HBLK (BITS_PER_HBLK/CPP_WORDSZ) + /* upper bound */ + /* We allocate 1 bit/word. Only the first word */ + /* in each object is actually marked. */ + +# ifdef ALIGN_DOUBLE +# define MARK_BITS_SZ (((MARK_BITS_PER_HBLK + 2*CPP_WORDSZ - 1) \ + / (2*CPP_WORDSZ))*2) +# else +# define MARK_BITS_SZ ((MARK_BITS_PER_HBLK + CPP_WORDSZ - 1)/CPP_WORDSZ) +# endif + /* Upper bound on number of mark words per heap block */ + +struct hblkhdr { + word hb_sz; /* If in use, size in words, of objects in the block. */ + /* if free, the size in bytes of the whole block */ + struct hblk * hb_next; /* Link field for hblk free list */ + /* and for lists of chunks waiting to be */ + /* reclaimed. */ + struct hblk * hb_prev; /* Backwards link for free list. */ + word hb_descr; /* object descriptor for marking. See */ + /* mark.h. */ + char* hb_map; /* A pointer to a pointer validity map of the block. */ + /* See GC_obj_map. */ + /* Valid for all blocks with headers. */ + /* Free blocks point to GC_invalid_map. */ + unsigned char hb_obj_kind; + /* Kind of objects in the block. Each kind */ + /* identifies a mark procedure and a set of */ + /* list headers. Sometimes called regions. */ + unsigned char hb_flags; +# define IGNORE_OFF_PAGE 1 /* Ignore pointers that do not */ + /* point to the first page of */ + /* this object. */ +# define WAS_UNMAPPED 2 /* This is a free block, which has */ + /* been unmapped from the address */ + /* space. */ + /* GC_remap must be invoked on it */ + /* before it can be reallocated. */ + /* Only set with USE_MUNMAP. */ + unsigned short hb_last_reclaimed; + /* Value of GC_gc_no when block was */ + /* last allocated or swept. May wrap. */ + /* For a free block, this is maintained */ + /* unly for USE_MUNMAP, and indicates */ + /* when the header was allocated, or */ + /* when the size of the block last */ + /* changed. */ + word hb_marks[MARK_BITS_SZ]; + /* Bit i in the array refers to the */ + /* object starting at the ith word (header */ + /* INCLUDED) in the heap block. */ + /* The lsb of word 0 is numbered 0. */ +}; + +/* heap block body */ + +# define DISCARD_WORDS 0 + /* Number of words to be dropped at the beginning of each block */ + /* Must be a multiple of WORDSZ. May reasonably be nonzero */ + /* on machines that don't guarantee longword alignment of */ + /* pointers, so that the number of false hits is minimized. */ + /* 0 and WORDSZ are probably the only reasonable values. */ + +# define BODY_SZ ((HBLKSIZE-WORDS_TO_BYTES(DISCARD_WORDS))/sizeof(word)) + +struct hblk { +# if (DISCARD_WORDS != 0) + word garbage[DISCARD_WORDS]; +# endif + word hb_body[BODY_SZ]; +}; + +# define HDR_WORDS ((word)DISCARD_WORDS) +# define HDR_BYTES ((word)WORDS_TO_BYTES(DISCARD_WORDS)) + +# define OBJ_SZ_TO_BLOCKS(sz) \ + divHBLKSZ(HDR_BYTES + WORDS_TO_BYTES(sz) + HBLKSIZE-1) + /* Size of block (in units of HBLKSIZE) needed to hold objects of */ + /* given sz (in words). */ + +/* Object free list link */ +# define obj_link(p) (*(ptr_t *)(p)) + +/* The type of mark procedures. This really belongs in gc_mark.h. */ +/* But we put it here, so that we can avoid scanning the mark proc */ +/* table. */ +typedef struct ms_entry * (*mark_proc)(/* word * addr, mark_stack_ptr, + mark_stack_limit, env */); +# define LOG_MAX_MARK_PROCS 6 +# define MAX_MARK_PROCS (1 << LOG_MAX_MARK_PROCS) + +/* Root sets. Logically private to mark_rts.c. But we don't want the */ +/* tables scanned, so we put them here. */ +/* MAX_ROOT_SETS is the maximum number of ranges that can be */ +/* registered as static roots. */ +# ifdef LARGE_CONFIG +# define MAX_ROOT_SETS 4096 +# else +# ifdef PCR +# define MAX_ROOT_SETS 1024 +# else +# ifdef MSWIN32 +# define MAX_ROOT_SETS 512 + /* Under NT, we add only written pages, which can result */ + /* in many small root sets. */ +# else +# define MAX_ROOT_SETS 64 +# endif +# endif +# endif + +# define MAX_EXCLUSIONS (MAX_ROOT_SETS/4) +/* Maximum number of segments that can be excluded from root sets. */ + +/* + * Data structure for excluded static roots. + */ +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +struct roots { + ptr_t r_start; + ptr_t r_end; +# ifndef MSWIN32 + struct roots * r_next; +# endif + GC_bool r_tmp; + /* Delete before registering new dynamic libraries */ +}; + +#ifndef MSWIN32 + /* Size of hash table index to roots. */ +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) /* Power of 2, may be != MAX_ROOT_SETS */ +#endif + +/* Lists of all heap blocks and free lists */ +/* as well as other random data structures */ +/* that should not be scanned by the */ +/* collector. */ +/* These are grouped together in a struct */ +/* so that they can be easily skipped by the */ +/* GC_mark routine. */ +/* The ordering is weird to make GC_malloc */ +/* faster by keeping the important fields */ +/* sufficiently close together that a */ +/* single load of a base register will do. */ +/* Scalars that could easily appear to */ +/* be pointers are also put here. */ +/* The main fields should precede any */ +/* conditionally included fields, so that */ +/* gc_inl.h will work even if a different set */ +/* of macros is defined when the client is */ +/* compiled. */ + +struct _GC_arrays { + word _heapsize; + word _max_heapsize; + ptr_t _last_heap_addr; + ptr_t _prev_heap_addr; + word _large_free_bytes; + /* Total bytes contained in blocks on large object free */ + /* list. */ + word _words_allocd_before_gc; + /* Number of words allocated before this */ + /* collection cycle. */ + word _words_allocd; + /* Number of words allocated during this collection cycle */ + word _words_wasted; + /* Number of words wasted due to internal fragmentation */ + /* in large objects, or due to dropping blacklisted */ + /* blocks, since last gc. Approximate. */ + word _words_finalized; + /* Approximate number of words in objects (and headers) */ + /* That became ready for finalization in the last */ + /* collection. */ + word _non_gc_bytes_at_gc; + /* Number of explicitly managed bytes of storage */ + /* at last collection. */ + word _mem_freed; + /* Number of explicitly deallocated words of memory */ + /* since last collection. */ + mark_proc _mark_procs[MAX_MARK_PROCS]; + /* Table of user-defined mark procedures. There is */ + /* a small number of these, which can be referenced */ + /* by DS_PROC mark descriptors. See gc_mark.h. */ + ptr_t _objfreelist[MAXOBJSZ+1]; + /* free list for objects */ + ptr_t _aobjfreelist[MAXOBJSZ+1]; + /* free list for atomic objs */ + + ptr_t _uobjfreelist[MAXOBJSZ+1]; + /* uncollectable but traced objs */ + /* objects on this and auobjfreelist */ + /* are always marked, except during */ + /* garbage collections. */ +# ifdef ATOMIC_UNCOLLECTABLE + ptr_t _auobjfreelist[MAXOBJSZ+1]; +# endif + /* uncollectable but traced objs */ + +# ifdef GATHERSTATS + word _composite_in_use; + /* Number of words in accessible composite */ + /* objects. */ + word _atomic_in_use; + /* Number of words in accessible atomic */ + /* objects. */ +# endif +# ifdef USE_MUNMAP + word _unmapped_bytes; +# endif +# ifdef MERGE_SIZES + unsigned _size_map[WORDS_TO_BYTES(MAXOBJSZ+1)]; + /* Number of words to allocate for a given allocation request in */ + /* bytes. */ +# endif + +# ifdef STUBBORN_ALLOC + ptr_t _sobjfreelist[MAXOBJSZ+1]; +# endif + /* free list for immutable objects */ + ptr_t _obj_map[MAXOBJSZ+1]; + /* If not NIL, then a pointer to a map of valid */ + /* object addresses. _obj_map[sz][i] is j if the */ + /* address block_start+i is a valid pointer */ + /* to an object at */ + /* block_start+i&~3 - WORDS_TO_BYTES(j). */ + /* (If ALL_INTERIOR_POINTERS is defined, then */ + /* instead ((short *)(hb_map[sz])[i] is j if */ + /* block_start+WORDS_TO_BYTES(i) is in the */ + /* interior of an object starting at */ + /* block_start+WORDS_TO_BYTES(i-j)). */ + /* It is OBJ_INVALID if */ + /* block_start+WORDS_TO_BYTES(i) is not */ + /* valid as a pointer to an object. */ + /* We assume all values of j <= OBJ_INVALID. */ + /* The zeroth entry corresponds to large objects.*/ +# ifdef ALL_INTERIOR_POINTERS +# define map_entry_type short +# define OBJ_INVALID 0x7fff +# define MAP_ENTRY(map, bytes) \ + (((map_entry_type *)(map))[BYTES_TO_WORDS(bytes)]) +# define MAP_ENTRIES BYTES_TO_WORDS(HBLKSIZE) +# define MAP_SIZE (MAP_ENTRIES * sizeof(map_entry_type)) +# define OFFSET_VALID(displ) TRUE +# define CPP_MAX_OFFSET (HBLKSIZE - HDR_BYTES - 1) +# define MAX_OFFSET ((word)CPP_MAX_OFFSET) +# else +# define map_entry_type char +# define OBJ_INVALID 0x7f +# define MAP_ENTRY(map, bytes) \ + (map)[bytes] +# define MAP_ENTRIES HBLKSIZE +# define MAP_SIZE MAP_ENTRIES +# define CPP_MAX_OFFSET (WORDS_TO_BYTES(OBJ_INVALID) - 1) +# define MAX_OFFSET ((word)CPP_MAX_OFFSET) +# define VALID_OFFSET_SZ \ + (CPP_MAX_OFFSET > WORDS_TO_BYTES(CPP_MAXOBJSZ)? \ + CPP_MAX_OFFSET+1 \ + : WORDS_TO_BYTES(CPP_MAXOBJSZ)+1) + char _valid_offsets[VALID_OFFSET_SZ]; + /* GC_valid_offsets[i] == TRUE ==> i */ + /* is registered as a displacement. */ +# define OFFSET_VALID(displ) GC_valid_offsets[displ] + char _modws_valid_offsets[sizeof(word)]; + /* GC_valid_offsets[i] ==> */ + /* GC_modws_valid_offsets[i%sizeof(word)] */ +# endif +# ifdef STUBBORN_ALLOC + page_hash_table _changed_pages; + /* Stubborn object pages that were changes since last call to */ + /* GC_read_changed. */ + page_hash_table _prev_changed_pages; + /* Stubborn object pages that were changes before last call to */ + /* GC_read_changed. */ +# endif +# if defined(PROC_VDB) || defined(MPROTECT_VDB) + page_hash_table _grungy_pages; /* Pages that were dirty at last */ + /* GC_read_dirty. */ +# endif +# ifdef MPROTECT_VDB + VOLATILE page_hash_table _dirty_pages; + /* Pages dirtied since last GC_read_dirty. */ +# endif +# ifdef PROC_VDB + page_hash_table _written_pages; /* Pages ever dirtied */ +# endif +# ifdef LARGE_CONFIG +# if CPP_WORDSZ > 32 +# define MAX_HEAP_SECTS 4096 /* overflows at roughly 64 GB */ +# else +# define MAX_HEAP_SECTS 768 /* Separately added heap sections. */ +# endif +# else +# define MAX_HEAP_SECTS 256 +# endif + struct HeapSect { + ptr_t hs_start; word hs_bytes; + } _heap_sects[MAX_HEAP_SECTS]; +# ifdef MSWIN32 + ptr_t _heap_bases[MAX_HEAP_SECTS]; + /* Start address of memory regions obtained from kernel. */ +# endif + struct roots _static_roots[MAX_ROOT_SETS]; +# ifndef MSWIN32 + struct roots * _root_index[RT_SIZE]; +# endif + struct exclusion _excl_table[MAX_EXCLUSIONS]; + /* Block header index; see gc_headers.h */ + bottom_index * _all_nils; + bottom_index * _top_index [TOP_SZ]; +#ifdef SAVE_CALL_CHAIN + struct callinfo _last_stack[NFRAMES]; /* Stack at last garbage collection.*/ + /* Useful for debugging mysterious */ + /* object disappearances. */ + /* In the multithreaded case, we */ + /* currently only save the calling */ + /* stack. */ +#endif +}; + +GC_API GC_FAR struct _GC_arrays GC_arrays; + +# define GC_objfreelist GC_arrays._objfreelist +# define GC_aobjfreelist GC_arrays._aobjfreelist +# define GC_uobjfreelist GC_arrays._uobjfreelist +# ifdef ATOMIC_UNCOLLECTABLE +# define GC_auobjfreelist GC_arrays._auobjfreelist +# endif +# define GC_sobjfreelist GC_arrays._sobjfreelist +# define GC_valid_offsets GC_arrays._valid_offsets +# define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +# ifdef STUBBORN_ALLOC +# define GC_changed_pages GC_arrays._changed_pages +# define GC_prev_changed_pages GC_arrays._prev_changed_pages +# endif +# define GC_obj_map GC_arrays._obj_map +# define GC_last_heap_addr GC_arrays._last_heap_addr +# define GC_prev_heap_addr GC_arrays._prev_heap_addr +# define GC_words_allocd GC_arrays._words_allocd +# define GC_words_wasted GC_arrays._words_wasted +# define GC_large_free_bytes GC_arrays._large_free_bytes +# define GC_words_finalized GC_arrays._words_finalized +# define GC_non_gc_bytes_at_gc GC_arrays._non_gc_bytes_at_gc +# define GC_mem_freed GC_arrays._mem_freed +# define GC_mark_procs GC_arrays._mark_procs +# define GC_heapsize GC_arrays._heapsize +# define GC_max_heapsize GC_arrays._max_heapsize +# define GC_words_allocd_before_gc GC_arrays._words_allocd_before_gc +# define GC_heap_sects GC_arrays._heap_sects +# define GC_last_stack GC_arrays._last_stack +# ifdef USE_MUNMAP +# define GC_unmapped_bytes GC_arrays._unmapped_bytes +# endif +# ifdef MSWIN32 +# define GC_heap_bases GC_arrays._heap_bases +# endif +# define GC_static_roots GC_arrays._static_roots +# define GC_root_index GC_arrays._root_index +# define GC_excl_table GC_arrays._excl_table +# define GC_all_nils GC_arrays._all_nils +# define GC_top_index GC_arrays._top_index +# if defined(PROC_VDB) || defined(MPROTECT_VDB) +# define GC_grungy_pages GC_arrays._grungy_pages +# endif +# ifdef MPROTECT_VDB +# define GC_dirty_pages GC_arrays._dirty_pages +# endif +# ifdef PROC_VDB +# define GC_written_pages GC_arrays._written_pages +# endif +# ifdef GATHERSTATS +# define GC_composite_in_use GC_arrays._composite_in_use +# define GC_atomic_in_use GC_arrays._atomic_in_use +# endif +# ifdef MERGE_SIZES +# define GC_size_map GC_arrays._size_map +# endif + +# define beginGC_arrays ((ptr_t)(&GC_arrays)) +# define endGC_arrays (((ptr_t)(&GC_arrays)) + (sizeof GC_arrays)) + +/* Object kinds: */ +# define MAXOBJKINDS 16 + +extern struct obj_kind { + ptr_t *ok_freelist; /* Array of free listheaders for this kind of object */ + /* Point either to GC_arrays or to storage allocated */ + /* with GC_scratch_alloc. */ + struct hblk **ok_reclaim_list; + /* List headers for lists of blocks waiting to be */ + /* swept. */ + word ok_descriptor; /* Descriptor template for objects in this */ + /* block. */ + GC_bool ok_relocate_descr; + /* Add object size in bytes to descriptor */ + /* template to obtain descriptor. Otherwise */ + /* template is used as is. */ + GC_bool ok_init; /* Clear objects before putting them on the free list. */ +} GC_obj_kinds[MAXOBJKINDS]; + +# define endGC_obj_kinds (((ptr_t)(&GC_obj_kinds)) + (sizeof GC_obj_kinds)) + +# define end_gc_area ((ptr_t)endGC_arrays == (ptr_t)(&GC_obj_kinds) ? \ + endGC_obj_kinds : endGC_arrays) + +/* Predefined kinds: */ +# define PTRFREE 0 +# define NORMAL 1 +# define UNCOLLECTABLE 2 +# ifdef ATOMIC_UNCOLLECTABLE +# define AUNCOLLECTABLE 3 +# define STUBBORN 4 +# define IS_UNCOLLECTABLE(k) (((k) & ~1) == UNCOLLECTABLE) +# else +# define STUBBORN 3 +# define IS_UNCOLLECTABLE(k) ((k) == UNCOLLECTABLE) +# endif + +extern int GC_n_kinds; + +GC_API word GC_fo_entries; + +extern word GC_n_heap_sects; /* Number of separately added heap */ + /* sections. */ + +extern word GC_page_size; + +# ifdef MSWIN32 +extern word GC_n_heap_bases; /* See GC_heap_bases. */ +# endif + +extern word GC_total_stack_black_listed; + /* Number of bytes on stack blacklist. */ + +extern word GC_black_list_spacing; + /* Average number of bytes between blacklisted */ + /* blocks. Approximate. */ + /* Counts only blocks that are */ + /* "stack-blacklisted", i.e. that are */ + /* problematic in the interior of an object. */ + +extern char * GC_invalid_map; + /* Pointer to the nowhere valid hblk map */ + /* Blocks pointing to this map are free. */ + +extern struct hblk * GC_hblkfreelist[]; + /* List of completely empty heap blocks */ + /* Linked through hb_next field of */ + /* header structure associated with */ + /* block. */ + +extern GC_bool GC_is_initialized; /* GC_init() has been run. */ + +extern GC_bool GC_objects_are_marked; /* There are marked objects in */ + /* the heap. */ + +#ifndef SMALL_CONFIG + extern GC_bool GC_incremental; + /* Using incremental/generational collection. */ +#else +# define GC_incremental TRUE + /* Hopefully allow optimizer to remove some code. */ +#endif + +extern GC_bool GC_dirty_maintained; + /* Dirty bits are being maintained, */ + /* either for incremental collection, */ + /* or to limit the root set. */ + +extern word GC_root_size; /* Total size of registered root sections */ + +extern GC_bool GC_debugging_started; /* GC_debug_malloc has been called. */ + +extern ptr_t GC_least_plausible_heap_addr; +extern ptr_t GC_greatest_plausible_heap_addr; + /* Bounds on the heap. Guaranteed valid */ + /* Likely to include future heap expansion. */ + +/* Operations */ +# ifndef abs +# define abs(x) ((x) < 0? (-(x)) : (x)) +# endif + + +/* Marks are in a reserved area in */ +/* each heap block. Each word has one mark bit associated */ +/* with it. Only those corresponding to the beginning of an */ +/* object are used. */ + + +/* Mark bit operations */ + +/* + * Retrieve, set, clear the mark bit corresponding + * to the nth word in a given heap block. + * + * (Recall that bit n corresponds to object beginning at word n + * relative to the beginning of the block, including unused words) + */ + +# define mark_bit_from_hdr(hhdr,n) (((hhdr)->hb_marks[divWORDSZ(n)] \ + >> (modWORDSZ(n))) & (word)1) +# define set_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ + |= (word)1 << modWORDSZ(n) + +# define clear_mark_bit_from_hdr(hhdr,n) (hhdr)->hb_marks[divWORDSZ(n)] \ + &= ~((word)1 << modWORDSZ(n)) + +/* Important internal collector routines */ + +ptr_t GC_approx_sp(); + +GC_bool GC_should_collect(); +#ifdef PRESERVE_LAST + GC_bool GC_in_last_heap_sect(/* ptr_t */); + /* In last added heap section? If so, avoid breaking up. */ +#endif +void GC_apply_to_all_blocks(/*fn, client_data*/); + /* Invoke fn(hbp, client_data) for each */ + /* allocated heap block. */ +struct hblk * GC_next_used_block(/* struct hblk * h */); + /* Return first in-use block >= h */ +struct hblk * GC_prev_block(/* struct hblk * h */); + /* Return last block <= h. Returned block */ + /* is managed by GC, but may or may not be in */ + /* use. */ +void GC_mark_init(); +void GC_clear_marks(); /* Clear mark bits for all heap objects. */ +void GC_invalidate_mark_state(); /* Tell the marker that marked */ + /* objects may point to unmarked */ + /* ones, and roots may point to */ + /* unmarked objects. */ + /* Reset mark stack. */ +void GC_mark_from_mark_stack(); /* Mark from everything on the mark stack. */ + /* Return after about one pages worth of */ + /* work. */ +GC_bool GC_mark_stack_empty(); +GC_bool GC_mark_some(/* cold_gc_frame */); + /* Perform about one pages worth of marking */ + /* work of whatever kind is needed. Returns */ + /* quickly if no collection is in progress. */ + /* Return TRUE if mark phase finished. */ +void GC_initiate_gc(); /* initiate collection. */ + /* If the mark state is invalid, this */ + /* becomes full colleection. Otherwise */ + /* it's partial. */ +void GC_push_all(/*b,t*/); /* Push everything in a range */ + /* onto mark stack. */ +void GC_push_dirty(/*b,t*/); /* Push all possibly changed */ + /* subintervals of [b,t) onto */ + /* mark stack. */ +#ifndef SMALL_CONFIG + void GC_push_conditional(/* ptr_t b, ptr_t t, GC_bool all*/); +#else +# define GC_push_conditional(b, t, all) GC_push_all(b, t) +#endif + /* Do either of the above, depending */ + /* on the third arg. */ +void GC_push_all_stack(/*b,t*/); /* As above, but consider */ + /* interior pointers as valid */ +void GC_push_all_eager(/*b,t*/); /* Same as GC_push_all_stack, but */ + /* ensures that stack is scanned */ + /* immediately, not just scheduled */ + /* for scanning. */ +#ifndef THREADS + void GC_push_all_stack_partially_eager(/* bottom, top, cold_gc_frame */); + /* Similar to GC_push_all_eager, but only the */ + /* part hotter than cold_gc_frame is scanned */ + /* immediately. Needed to endure that callee- */ + /* save registers are not missed. */ +#else + /* In the threads case, we push part of the current thread stack */ + /* with GC_push_all_eager when we push the registers. This gets the */ + /* callee-save registers that may disappear. The remainder of the */ + /* stacks are scheduled for scanning in *GC_push_other_roots, which */ + /* is thread-package-specific. */ +#endif +void GC_push_current_stack(/* ptr_t cold_gc_frame */); + /* Push enough of the current stack eagerly to */ + /* ensure that callee-save registers saved in */ + /* GC frames are scanned. */ + /* In the non-threads case, schedule entire */ + /* stack for scanning. */ +void GC_push_roots(/* GC_bool all, ptr_t cold_gc_frame */); + /* Push all or dirty roots. */ +extern void (*GC_push_other_roots)(); + /* Push system or application specific roots */ + /* onto the mark stack. In some environments */ + /* (e.g. threads environments) this is */ + /* predfined to be non-zero. A client supplied */ + /* replacement should also call the original */ + /* function. */ +extern void (*GC_start_call_back)(/* void */); + /* Called at start of full collections. */ + /* Not called if 0. Called with allocation */ + /* lock held. */ + /* 0 by default. */ +void GC_push_regs(); /* Push register contents onto mark stack. */ +void GC_remark(); /* Mark from all marked objects. Used */ + /* only if we had to drop something. */ +# if defined(MSWIN32) + void __cdecl GC_push_one(); +# else + void GC_push_one(/*p*/); /* If p points to an object, mark it */ + /* and push contents on the mark stack */ +# endif +void GC_push_one_checked(/*p*/); /* Ditto, omits plausibility test */ +void GC_push_marked(/* struct hblk h, hdr * hhdr */); + /* Push contents of all marked objects in h onto */ + /* mark stack. */ +#ifdef SMALL_CONFIG +# define GC_push_next_marked_dirty(h) GC_push_next_marked(h) +#else + struct hblk * GC_push_next_marked_dirty(/* h */); + /* Invoke GC_push_marked on next dirty block above h. */ + /* Return a pointer just past the end of this block. */ +#endif /* !SMALL_CONFIG */ +struct hblk * GC_push_next_marked(/* h */); + /* Ditto, but also mark from clean pages. */ +struct hblk * GC_push_next_marked_uncollectable(/* h */); + /* Ditto, but mark only from uncollectable pages. */ +GC_bool GC_stopped_mark(); /* Stop world and mark from all roots */ + /* and rescuers. */ +void GC_clear_hdr_marks(/* hhdr */); /* Clear the mark bits in a header */ +void GC_set_hdr_marks(/* hhdr */); /* Set the mark bits in a header */ +void GC_add_roots_inner(); +GC_bool GC_is_static_root(/* ptr_t p */); + /* Is the address p in one of the registered static */ + /* root sections? */ +void GC_register_dynamic_libraries(); + /* Add dynamic library data sections to the root set. */ + +/* Machine dependent startup routines */ +ptr_t GC_get_stack_base(); +void GC_register_data_segments(); + +/* Black listing: */ +void GC_bl_init(); +# ifndef ALL_INTERIOR_POINTERS + void GC_add_to_black_list_normal(/* bits, maybe source */); + /* Register bits as a possible future false */ + /* reference from the heap or static data */ +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_normal(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_normal(bits) +# endif +# else +# ifdef PRINT_BLACK_LIST +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits, source) +# else +# define GC_ADD_TO_BLACK_LIST_NORMAL(bits, source) \ + GC_add_to_black_list_stack(bits) +# endif +# endif + +void GC_add_to_black_list_stack(/* bits, maybe source */); +struct hblk * GC_is_black_listed(/* h, len */); + /* If there are likely to be false references */ + /* to a block starting at h of the indicated */ + /* length, then return the next plausible */ + /* starting location for h that might avoid */ + /* these false references. */ +void GC_promote_black_lists(); + /* Declare an end to a black listing phase. */ +void GC_unpromote_black_lists(); + /* Approximately undo the effect of the above. */ + /* This actually loses some information, but */ + /* only in a reasonably safe way. */ +word GC_number_stack_black_listed(/*struct hblk *start, struct hblk *endp1 */); + /* Return the number of (stack) blacklisted */ + /* blocks in the range for statistical */ + /* purposes. */ + +ptr_t GC_scratch_alloc(/*bytes*/); + /* GC internal memory allocation for */ + /* small objects. Deallocation is not */ + /* possible. */ + +/* Heap block layout maps: */ +void GC_invalidate_map(/* hdr */); + /* Remove the object map associated */ + /* with the block. This identifies */ + /* the block as invalid to the mark */ + /* routines. */ +GC_bool GC_add_map_entry(/*sz*/); + /* Add a heap block map for objects of */ + /* size sz to obj_map. */ + /* Return FALSE on failure. */ +void GC_register_displacement_inner(/*offset*/); + /* Version of GC_register_displacement */ + /* that assumes lock is already held */ + /* and signals are already disabled. */ + +/* hblk allocation: */ +void GC_new_hblk(/*size_in_words, kind*/); + /* Allocate a new heap block, and build */ + /* a free list in it. */ +struct hblk * GC_allochblk(/*size_in_words, kind*/); + /* Allocate a heap block, clear it if */ + /* for composite objects, inform */ + /* the marker that block is valid */ + /* for objects of indicated size. */ + /* sz < 0 ==> atomic. */ +void GC_freehblk(); /* Deallocate a heap block and mark it */ + /* as invalid. */ + +/* Misc GC: */ +void GC_init_inner(); +GC_bool GC_expand_hp_inner(); +void GC_start_reclaim(/*abort_if_found*/); + /* Restore unmarked objects to free */ + /* lists, or (if abort_if_found is */ + /* TRUE) report them. */ + /* Sweeping of small object pages is */ + /* largely deferred. */ +void GC_continue_reclaim(/*size, kind*/); + /* Sweep pages of the given size and */ + /* kind, as long as possible, and */ + /* as long as the corr. free list is */ + /* empty. */ +void GC_reclaim_or_delete_all(); + /* Arrange for all reclaim lists to be */ + /* empty. Judiciously choose between */ + /* sweeping and discarding each page. */ +GC_bool GC_reclaim_all(/* GC_stop_func f*/); + /* Reclaim all blocks. Abort (in a */ + /* consistent state) if f returns TRUE. */ +GC_bool GC_block_empty(/* hhdr */); /* Block completely unmarked? */ +GC_bool GC_never_stop_func(); /* Returns FALSE. */ +GC_bool GC_try_to_collect_inner(/* GC_stop_func f */); + /* Collect; caller must have acquired */ + /* lock and disabled signals. */ + /* Collection is aborted if f returns */ + /* TRUE. Returns TRUE if it completes */ + /* successfully. */ +# define GC_gcollect_inner() \ + (void) GC_try_to_collect_inner(GC_never_stop_func) +void GC_finish_collection(); /* Finish collection. Mark bits are */ + /* consistent and lock is still held. */ +GC_bool GC_collect_or_expand(/* needed_blocks */); + /* Collect or expand heap in an attempt */ + /* make the indicated number of free */ + /* blocks available. Should be called */ + /* until the blocks are available or */ + /* until it fails by returning FALSE. */ +GC_API void GC_init(); /* Initialize collector. */ +void GC_collect_a_little_inner(/* int n */); + /* Do n units worth of garbage */ + /* collection work, if appropriate. */ + /* A unit is an amount appropriate for */ + /* HBLKSIZE bytes of allocation. */ +ptr_t GC_generic_malloc(/* bytes, kind */); + /* Allocate an object of the given */ + /* kind. By default, there are only */ + /* a few kinds: composite(pointerfree), */ + /* atomic, uncollectable, etc. */ + /* We claim it's possible for clever */ + /* client code that understands GC */ + /* internals to add more, e.g. to */ + /* communicate object layout info */ + /* to the collector. */ +ptr_t GC_generic_malloc_ignore_off_page(/* bytes, kind */); + /* As above, but pointers past the */ + /* first page of the resulting object */ + /* are ignored. */ +ptr_t GC_generic_malloc_inner(/* bytes, kind */); + /* Ditto, but I already hold lock, etc. */ +ptr_t GC_generic_malloc_words_small GC_PROTO((size_t words, int kind)); + /* As above, but size in units of words */ + /* Bypasses MERGE_SIZES. Assumes */ + /* words <= MAXOBJSZ. */ +ptr_t GC_generic_malloc_inner_ignore_off_page(/* bytes, kind */); + /* Allocate an object, where */ + /* the client guarantees that there */ + /* will always be a pointer to the */ + /* beginning of the object while the */ + /* object is live. */ +ptr_t GC_allocobj(/* sz_inn_words, kind */); + /* Make the indicated */ + /* free list nonempty, and return its */ + /* head. */ + +void GC_init_headers(); +GC_bool GC_install_header(/*h*/); + /* Install a header for block h. */ + /* Return FALSE on failure. */ +GC_bool GC_install_counts(/*h, sz*/); + /* Set up forwarding counts for block */ + /* h of size sz. */ + /* Return FALSE on failure. */ +void GC_remove_header(/*h*/); + /* Remove the header for block h. */ +void GC_remove_counts(/*h, sz*/); + /* Remove forwarding counts for h. */ +hdr * GC_find_header(/*p*/); /* Debugging only. */ + +void GC_finalize(); /* Perform all indicated finalization actions */ + /* on unmarked objects. */ + /* Unreachable finalizable objects are enqueued */ + /* for processing by GC_invoke_finalizers. */ + /* Invoked with lock. */ + +void GC_add_to_heap(/*p, bytes*/); + /* Add a HBLKSIZE aligned chunk to the heap. */ + +void GC_print_obj(/* ptr_t p */); + /* P points to somewhere inside an object with */ + /* debugging info. Print a human readable */ + /* description of the object to stderr. */ +extern void (*GC_check_heap)(); + /* Check that all objects in the heap with */ + /* debugging info are intact. Print */ + /* descriptions of any that are not. */ +extern void (*GC_print_heap_obj)(/* ptr_t p */); + /* If possible print s followed by a more */ + /* detailed description of the object */ + /* referred to by p. */ + +/* Memory unmapping: */ +#ifdef USE_MUNMAP + void GC_unmap_old(void); + void GC_merge_unmapped(void); + void GC_unmap(ptr_t start, word bytes); + void GC_remap(ptr_t start, word bytes); + void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2); +#endif + +/* Virtual dirty bit implementation: */ +/* Each implementation exports the following: */ +void GC_read_dirty(); /* Retrieve dirty bits. */ +GC_bool GC_page_was_dirty(/* struct hblk * h */); + /* Read retrieved dirty bits. */ +GC_bool GC_page_was_ever_dirty(/* struct hblk * h */); + /* Could the page contain valid heap pointers? */ +void GC_is_fresh(/* struct hblk * h, word number_of_blocks */); + /* Assert the region currently contains no */ + /* valid pointers. */ +void GC_write_hint(/* struct hblk * h */); + /* h is about to be written. */ +void GC_dirty_init(); + +/* Slow/general mark bit manipulation: */ +GC_API GC_bool GC_is_marked(); +void GC_clear_mark_bit(); +void GC_set_mark_bit(); + +/* Stubborn objects: */ +void GC_read_changed(); /* Analogous to GC_read_dirty */ +GC_bool GC_page_was_changed(/* h */); /* Analogous to GC_page_was_dirty */ +void GC_clean_changing_list(); /* Collect obsolete changing list entries */ +void GC_stubborn_init(); + +/* Debugging print routines: */ +void GC_print_block_list(); +void GC_print_hblkfreelist(); +void GC_print_heap_sects(); +void GC_print_static_roots(); +void GC_dump(); + +#ifdef KEEP_BACK_PTRS + void GC_store_back_pointer(ptr_t source, ptr_t dest); + void GC_marked_for_finalization(ptr_t dest); +# define GC_STORE_BACK_PTR(source, dest) GC_store_back_pointer(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) GC_marked_for_finalization(dest) +#else +# define GC_STORE_BACK_PTR(source, dest) +# define GC_MARKED_FOR_FINALIZATION(dest) +#endif + +/* Make arguments appear live to compiler */ +# ifdef __WATCOMC__ + void GC_noop(void*, ...); +# else + GC_API void GC_noop(); +# endif + +void GC_noop1(/* word arg */); + +/* Logging and diagnostic output: */ +GC_API void GC_printf GC_PROTO((char * format, long, long, long, long, long, long)); + /* A version of printf that doesn't allocate, */ + /* is restricted to long arguments, and */ + /* (unfortunately) doesn't use varargs for */ + /* portability. Restricted to 6 args and */ + /* 1K total output length. */ + /* (We use sprintf. Hopefully that doesn't */ + /* allocate for long arguments.) */ +# define GC_printf0(f) GC_printf(f, 0l, 0l, 0l, 0l, 0l, 0l) +# define GC_printf1(f,a) GC_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) +# define GC_printf2(f,a,b) GC_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) +# define GC_printf3(f,a,b,c) GC_printf(f, (long)a, (long)b, (long)c, 0l, 0l, 0l) +# define GC_printf4(f,a,b,c,d) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, 0l, 0l) +# define GC_printf5(f,a,b,c,d,e) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, (long)e, 0l) +# define GC_printf6(f,a,b,c,d,e,g) GC_printf(f, (long)a, (long)b, (long)c, \ + (long)d, (long)e, (long)g) + +void GC_err_printf(/* format, a, b, c, d, e, f */); +# define GC_err_printf0(f) GC_err_puts(f) +# define GC_err_printf1(f,a) GC_err_printf(f, (long)a, 0l, 0l, 0l, 0l, 0l) +# define GC_err_printf2(f,a,b) GC_err_printf(f, (long)a, (long)b, 0l, 0l, 0l, 0l) +# define GC_err_printf3(f,a,b,c) GC_err_printf(f, (long)a, (long)b, (long)c, \ + 0l, 0l, 0l) +# define GC_err_printf4(f,a,b,c,d) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, 0l, 0l) +# define GC_err_printf5(f,a,b,c,d,e) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, 0l) +# define GC_err_printf6(f,a,b,c,d,e,g) GC_err_printf(f, (long)a, (long)b, \ + (long)c, (long)d, \ + (long)e, (long)g) + /* Ditto, writes to stderr. */ + +void GC_err_puts(/* char *s */); + /* Write s to stderr, don't buffer, don't add */ + /* newlines, don't ... */ + + +# ifdef GC_ASSERTIONS +# define GC_ASSERT(expr) if(!(expr)) {\ + GC_err_printf2("Assertion failure: %s:%ld\n", \ + __FILE__, (unsigned long)__LINE__); \ + ABORT("assertion failure"); } +# else +# define GC_ASSERT(expr) +# endif + +# endif /* GC_PRIVATE_H */ diff --git a/gc/include/private/gcconfig.h b/gc/include/private/gcconfig.h new file mode 100644 index 0000000..c9017d3 --- /dev/null +++ b/gc/include/private/gcconfig.h @@ -0,0 +1,1099 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef CONFIG_H + +# define CONFIG_H + +/* Machine dependent parameters. Some tuning parameters can be found */ +/* near the top of gc_private.h. */ + +/* Machine specific parts contributed by various people. See README file. */ + +/* First a unified test for Linux: */ +# if defined(linux) || defined(__linux__) +# define LINUX +# endif + +/* Determine the machine type: */ +# if defined(sun) && defined(mc68000) +# define M68K +# define SUNOS4 +# define mach_type_known +# endif +# if defined(hp9000s300) +# define M68K +# define HP +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(m68k) +# define M68K +# define OPENBSD +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(__sparc__) +# define SPARC +# define OPENBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(m68k) +# define M68K +# define NETBSD +# define mach_type_known +# endif +# if defined(vax) +# define VAX +# ifdef ultrix +# define ULTRIX +# else +# define BSD +# endif +# define mach_type_known +# endif +# if defined(mips) || defined(__mips) +# define MIPS +# if defined(ultrix) || defined(__ultrix) || defined(__NetBSD__) +# define ULTRIX +# else +# if defined(_SYSTYPE_SVR4) || defined(SYSTYPE_SVR4) || defined(__SYSTYPE_SVR4__) +# define IRIX5 /* or IRIX 6.X */ +# else +# define RISCOS /* or IRIX 4.X */ +# endif +# endif +# define mach_type_known +# endif +# if defined(sequent) && defined(i386) +# define I386 +# define SEQUENT +# define mach_type_known +# endif +# if defined(sun) && defined(i386) +# define I386 +# define SUNOS5 +# define mach_type_known +# endif +# if (defined(__OS2__) || defined(__EMX__)) && defined(__32BIT__) +# define I386 +# define OS2 +# define mach_type_known +# endif +# if defined(ibm032) +# define RT +# define mach_type_known +# endif +# if defined(sun) && (defined(sparc) || defined(__sparc)) +# define SPARC + /* Test for SunOS 5.x */ +# include <errno.h> +# ifdef ECHRNG +# define SUNOS5 +# else +# define SUNOS4 +# endif +# define mach_type_known +# endif +# if defined(sparc) && defined(unix) && !defined(sun) && !defined(linux) \ + && !defined(__OpenBSD__) +# define SPARC +# define DRSNX +# define mach_type_known +# endif +# if defined(_IBMR2) +# define RS6000 +# define mach_type_known +# endif +# if defined(_M_XENIX) && defined(_M_SYSV) && defined(_M_I386) + /* The above test may need refinement */ +# define I386 +# if defined(_SCO_ELF) +# define SCO_ELF +# else +# define SCO +# endif +# define mach_type_known +# endif +# if defined(_AUX_SOURCE) +# define M68K +# define SYSV +# define mach_type_known +# endif +# if defined(_PA_RISC1_0) || defined(_PA_RISC1_1) \ + || defined(hppa) || defined(__hppa__) +# define HP_PA +# define mach_type_known +# endif +# if defined(LINUX) && (defined(i386) || defined(__i386__)) +# define I386 +# define mach_type_known +# endif +# if defined(LINUX) && defined(powerpc) +# define POWERPC +# define mach_type_known +# endif +# if defined(LINUX) && defined(__mc68000__) +# define M68K +# define mach_type_known +# endif +# if defined(LINUX) && defined(sparc) +# define SPARC +# define mach_type_known +# endif +# if defined(__alpha) || defined(__alpha__) +# define ALPHA +# if !defined(LINUX) +# define OSF1 /* a.k.a Digital Unix */ +# endif +# define mach_type_known +# endif +# if defined(_AMIGA) && !defined(AMIGA) +# define AMIGA +# endif +# ifdef AMIGA +# define M68K +# define mach_type_known +# endif +# if defined(THINK_C) || defined(__MWERKS__) && !defined(__powerc) +# define M68K +# define MACOS +# define mach_type_known +# endif +# if defined(__MWERKS__) && defined(__powerc) +# define POWERPC +# define MACOS +# define mach_type_known +# endif +# if defined(macosx) +# define MACOSX +# define POWERPC +# define mach_type_known +# endif +# if defined(NeXT) && defined(mc68000) +# define M68K +# define NEXT +# define mach_type_known +# endif +# if defined(NeXT) && defined(i386) +# define I386 +# define NEXT +# define mach_type_known +# endif +# if defined(__OpenBSD__) && defined(i386) +# define I386 +# define OPENBSD +# define mach_type_known +# endif +# if defined(__FreeBSD__) && defined(i386) +# define I386 +# define FREEBSD +# define mach_type_known +# endif +# if defined(__NetBSD__) && defined(i386) +# define I386 +# define NETBSD +# define mach_type_known +# endif +# if defined(bsdi) && defined(i386) +# define I386 +# define BSDI +# define mach_type_known +# endif +# if !defined(mach_type_known) && defined(__386BSD__) +# define I386 +# define THREE86BSD +# define mach_type_known +# endif +# if defined(_CX_UX) && defined(_M88K) +# define M88K +# define CX_UX +# define mach_type_known +# endif +# if defined(DGUX) +# define M88K + /* DGUX defined */ +# define mach_type_known +# endif +# if (defined(_MSDOS) || defined(_MSC_VER)) && (_M_IX86 >= 300) \ + || defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__) +# define I386 +# define MSWIN32 /* or Win32s */ +# define mach_type_known +# endif +# if defined(__DJGPP__) +# define I386 +# ifndef DJGPP +# define DJGPP /* MSDOS running the DJGPP port of GCC */ +# endif +# define mach_type_known +# endif +# if defined(__CYGWIN32__) || defined(__CYGWIN__) +# define I386 +# define CYGWIN32 +# define mach_type_known +# endif +# if defined(__BORLANDC__) +# define I386 +# define MSWIN32 +# define mach_type_known +# endif +# if defined(_UTS) && !defined(mach_type_known) +# define S370 +# define UTS4 +# define mach_type_known +# endif +/* Ivan Demakov */ +# if defined(__WATCOMC__) && defined(__386__) +# define I386 +# if !defined(OS2) && !defined(MSWIN32) && !defined(DOS4GW) +# if defined(__OS2__) +# define OS2 +# else +# if defined(__WINDOWS_386__) || defined(__NT__) +# define MSWIN32 +# else +# define DOS4GW +# endif +# endif +# endif +# define mach_type_known +# endif + +/* Feel free to add more clauses here */ + +/* Or manually define the machine type here. A machine type is */ +/* characterized by the architecture. Some */ +/* machine types are further subdivided by OS. */ +/* the macros ULTRIX, RISCOS, and BSD to distinguish. */ +/* Note that SGI IRIX is treated identically to RISCOS. */ +/* SYSV on an M68K actually means A/UX. */ +/* The distinction in these cases is usually the stack starting address */ +# ifndef mach_type_known + --> unknown machine type +# endif + /* Mapping is: M68K ==> Motorola 680X0 */ + /* (SUNOS4,HP,NEXT, and SYSV (A/UX), */ + /* MACOS and AMIGA variants) */ + /* I386 ==> Intel 386 */ + /* (SEQUENT, OS2, SCO, LINUX, NETBSD, */ + /* FREEBSD, THREE86BSD, MSWIN32, */ + /* BSDI,SUNOS5, NEXT, other variants) */ + /* NS32K ==> Encore Multimax */ + /* MIPS ==> R2000 or R3000 */ + /* (RISCOS, ULTRIX variants) */ + /* VAX ==> DEC VAX */ + /* (BSD, ULTRIX variants) */ + /* RS6000 ==> IBM RS/6000 AIX3.X */ + /* RT ==> IBM PC/RT */ + /* HP_PA ==> HP9000/700 & /800 */ + /* HP/UX */ + /* SPARC ==> SPARC under SunOS */ + /* (SUNOS4, SUNOS5, */ + /* DRSNX variants) */ + /* ALPHA ==> DEC Alpha */ + /* (OSF1 and LINUX variants) */ + /* M88K ==> Motorola 88XX0 */ + /* (CX_UX and DGUX) */ + /* S370 ==> 370-like machine */ + /* running Amdahl UTS4 */ + + +/* + * For each architecture and OS, the following need to be defined: + * + * CPP_WORD_SZ is a simple integer constant representing the word size. + * in bits. We assume byte addressibility, where a byte has 8 bits. + * We also assume CPP_WORD_SZ is either 32 or 64. + * (We care about the length of pointers, not hardware + * bus widths. Thus a 64 bit processor with a C compiler that uses + * 32 bit pointers should use CPP_WORD_SZ of 32, not 64. Default is 32.) + * + * MACH_TYPE is a string representation of the machine type. + * OS_TYPE is analogous for the OS. + * + * ALIGNMENT is the largest N, such that + * all pointer are guaranteed to be aligned on N byte boundaries. + * defining it to be 1 will always work, but perform poorly. + * + * DATASTART is the beginning of the data segment. + * On UNIX systems, the collector will scan the area between DATASTART + * and DATAEND for root pointers. + * + * DATAEND, if not &end. + * + * ALIGN_DOUBLE of GC_malloc should return blocks aligned to twice + * the pointer size. + * + * STACKBOTTOM is the cool end of the stack, which is usually the + * highest address in the stack. + * Under PCR or OS/2, we have other ways of finding thread stacks. + * For each machine, the following should: + * 1) define STACK_GROWS_UP if the stack grows toward higher addresses, and + * 2) define exactly one of + * STACKBOTTOM (should be defined to be an expression) + * HEURISTIC1 + * HEURISTIC2 + * If either of the last two macros are defined, then STACKBOTTOM is computed + * during collector startup using one of the following two heuristics: + * HEURISTIC1: Take an address inside GC_init's frame, and round it up to + * the next multiple of STACK_GRAN. + * HEURISTIC2: Take an address inside GC_init's frame, increment it repeatedly + * in small steps (decrement if STACK_GROWS_UP), and read the value + * at each location. Remember the value when the first + * Segmentation violation or Bus error is signalled. Round that + * to the nearest plausible page boundary, and use that instead + * of STACKBOTTOM. + * + * If no expression for STACKBOTTOM can be found, and neither of the above + * heuristics are usable, the collector can still be used with all of the above + * undefined, provided one of the following is done: + * 1) GC_mark_roots can be changed to somehow mark from the correct stack(s) + * without reference to STACKBOTTOM. This is appropriate for use in + * conjunction with thread packages, since there will be multiple stacks. + * (Allocating thread stacks in the heap, and treating them as ordinary + * heap data objects is also possible as a last resort. However, this is + * likely to introduce significant amounts of excess storage retention + * unless the dead parts of the thread stacks are periodically cleared.) + * 2) Client code may set GC_stackbottom before calling any GC_ routines. + * If the author of the client code controls the main program, this is + * easily accomplished by introducing a new main program, setting + * GC_stackbottom to the address of a local variable, and then calling + * the original main program. The new main program would read something + * like: + * + * # include "gc_private.h" + * + * main(argc, argv, envp) + * int argc; + * char **argv, **envp; + * { + * int dummy; + * + * GC_stackbottom = (ptr_t)(&dummy); + * return(real_main(argc, argv, envp)); + * } + * + * + * Each architecture may also define the style of virtual dirty bit + * implementation to be used: + * MPROTECT_VDB: Write protect the heap and catch faults. + * PROC_VDB: Use the SVR4 /proc primitives to read dirty bits. + * + * An architecture may define DYNAMIC_LOADING if dynamic_load.c + * defined GC_register_dynamic_libraries() for the architecture. + */ + + +# define STACK_GRAN 0x1000000 +# ifdef M68K +# define MACH_TYPE "M68K" +# define ALIGNMENT 2 +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define STACKBOTTOM ((ptr_t)0xf0000000) +# define MPROTECT_VDB +# ifdef __ELF__ +# define DYNAMIC_LOADING + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + extern char etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x1ffff) & ~0x1ffff)) +# define HEURISTIC1 /* differs */ +# define DYNAMIC_LOADING +# endif +# ifdef HP +# define OS_TYPE "HP" + extern char etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t) 0xffeffffc) + /* empirically determined. seems to work. */ +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) +# endif +# ifdef SYSV +# define OS_TYPE "SYSV" + extern etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)&etext & 0x1fff)) + /* This only works for shared-text binaries with magic number 0413. + The other sorts of SysV binaries put the data at the end of the text, + in which case the default of &etext would work. Unfortunately, + handling both would require having the magic-number available. + -- Parag + */ +# define STACKBOTTOM ((ptr_t)0xFFFFFFFE) + /* The stack starts at the top of memory, but */ + /* 0x0 cannot be used as setjump_test complains */ + /* that the stack direction is incorrect. Two */ + /* bytes down from 0x0 should be safe enough. */ + /* --Parag */ +# include <sys/mmu.h> +# define GETPAGESIZE() PAGESIZE /* Is this still right? */ +# endif +# ifdef AMIGA +# define OS_TYPE "AMIGA" + /* STACKBOTTOM and DATASTART handled specially */ + /* in os_dep.c */ +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef MACOS +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# define GETPAGESIZE() 4096 +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t) 0x4000000) +# define DATAEND /* not needed */ +# endif +# endif + +# ifdef POWERPC +# define MACH_TYPE "POWERPC" +# ifdef MACOS +# define ALIGNMENT 2 /* Still necessary? Could it be 4? */ +# ifndef __LOWMEM__ +# include <LowMem.h> +# endif +# define OS_TYPE "MACOS" + /* see os_dep.c for details of global data segments. */ +# define STACKBOTTOM ((ptr_t) LMGetCurStackBase()) +# define DATAEND /* not needed */ +# endif +# ifdef LINUX +# define ALIGNMENT 4 /* Guess. Can someone verify? */ + /* This was 2, but that didn't sound right. */ +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 + /* Stack usually starts at 0x80000000 */ +# define DATASTART GC_data_start + extern int _end; +# define DATAEND (&_end) +# endif +# ifdef MACOSX +# define ALIGNMENT 4 +# define OS_TYPE "MACOSX" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t) 0xc0000000) +# define DATAEND /* not needed */ +# endif +# endif + +# ifdef VAX +# define MACH_TYPE "VAX" +# define ALIGNMENT 4 /* Pointers are longword aligned by 4.2 C compiler */ + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# ifdef BSD +# define OS_TYPE "BSD" +# define HEURISTIC1 + /* HEURISTIC2 may be OK, but it's hard to test. */ +# endif +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define STACKBOTTOM ((ptr_t) 0x7fffc800) +# endif +# endif + +# ifdef RT +# define MACH_TYPE "RT" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t) 0x10000000) +# define STACKBOTTOM ((ptr_t) 0x1fffd800) +# endif + +# ifdef SPARC +# define MACH_TYPE "SPARC" +# define ALIGNMENT 4 /* Required by hardware */ +# define ALIGN_DOUBLE + extern int etext; +# ifdef SUNOS5 +# define OS_TYPE "SUNOS5" + extern int _etext; + extern int _end; + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +# define DATAEND (&_end) +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# define PROC_VDB +/* HEURISTIC1 reportedly no longer works under 2.7. Thus we */ +/* switched to HEURISTIC2, eventhough it creates some debugging */ +/* issues. */ +# define HEURISTIC2 +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) + /* getpagesize() appeared to be missing from at least one */ + /* Solaris 5.4 installation. Weird. */ +# define DYNAMIC_LOADING +# endif +# ifdef SUNOS4 +# define OS_TYPE "SUNOS4" + /* [If you have a weak stomach, don't read this.] */ + /* We would like to use: */ +/* # define DATASTART ((ptr_t)((((word) (&etext)) + 0x1fff) & ~0x1fff)) */ + /* This fails occasionally, due to an ancient, but very */ + /* persistent ld bug. &etext is set 32 bytes too high. */ + /* We instead read the text segment size from the a.out */ + /* header, which happens to be mapped into our address space */ + /* at the start of the text segment. The detective work here */ + /* was done by Robert Ehrlich, Manuel Serrano, and Bernard */ + /* Serpette of INRIA. */ + /* This assumes ZMAGIC, i.e. demand-loadable executables. */ +# define TEXTSTART 0x2000 +# define DATASTART ((ptr_t)(*(int *)(TEXTSTART+0x4)+TEXTSTART)) +# define MPROTECT_VDB +# define HEURISTIC1 +# define DYNAMIC_LOADING +# endif +# ifdef DRSNX +# define CPP_WORDSZ 32 +# define OS_TYPE "DRSNX" + extern char * GC_SysVGetDataStart(); + extern int etext; +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) +# define MPROTECT_VDB +# define STACKBOTTOM ((ptr_t) 0xdfff0000) +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# ifdef __ELF__ +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else + Linux Sparc non elf ? +# endif + extern int _end; +# define DATAEND (&_end) +# define SVR4 +# define STACKBOTTOM ((ptr_t) 0xf0000000) +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# define STACKBOTTOM ((ptr_t) 0xf8000000) +# define DATASTART ((ptr_t)(&etext)) +# endif +# endif + +# ifdef I386 +# define MACH_TYPE "I386" +# define ALIGNMENT 4 /* Appears to hold for all "32 bit" compilers */ + /* except Borland. The -a4 option fixes */ + /* Borland. */ + /* Ivan Demakov: For Watcom the option is -zp4. */ +# ifndef SMALL_CONFIG +# define ALIGN_DOUBLE /* Not strictly necessary, but may give speed */ + /* improvement on Pentiums. */ +# endif +# ifdef SEQUENT +# define OS_TYPE "SEQUENT" + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# define STACKBOTTOM ((ptr_t) 0x3ffff000) +# endif +# ifdef SUNOS5 +# define OS_TYPE "SUNOS5" + extern int etext, _start; + extern char * GC_SysVGetDataStart(); +# define DATASTART GC_SysVGetDataStart(0x1000, &etext) +# define STACKBOTTOM ((ptr_t)(&_start)) +/** At least in Solaris 2.5, PROC_VDB gives wrong values for dirty bits. */ +/*# define PROC_VDB*/ +# define DYNAMIC_LOADING +# ifndef USE_MMAP +# define USE_MMAP +# endif +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x40000000 +# else +# define HEAP_START DATAEND +# endif +# endif +# ifdef SCO +# define OS_TYPE "SCO" + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x3fffff) \ + & ~0x3fffff) \ + +((word)&etext & 0xfff)) +# define STACKBOTTOM ((ptr_t) 0x7ffffffc) +# endif +# ifdef SCO_ELF +# define OS_TYPE "SCO_ELF" + extern int etext; +# define DATASTART ((ptr_t)(&etext)) +# define STACKBOTTOM ((ptr_t) 0x08048000) +# define DYNAMIC_LOADING +# define ELF_CLASS ELFCLASS32 +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define HEURISTIC1 +# undef STACK_GRAN +# define STACK_GRAN 0x10000000 + /* STACKBOTTOM is usually 0xc0000000, but this changes with */ + /* different kernel configurations. In particular, systems */ + /* with 2GB physical memory will usually move the user */ + /* address space limit, and hence initial SP to 0x80000000. */ +# if !defined(LINUX_THREADS) || !defined(REDIRECT_MALLOC) +# define MPROTECT_VDB +# else + /* We seem to get random errors in incremental mode, */ + /* possibly because Linux threads is itself a malloc client */ + /* and can't deal with the signals. */ +# endif +# ifdef __ELF__ +# define DYNAMIC_LOADING +# ifdef UNDEFINED /* includes ro data */ + extern int _etext; +# define DATASTART ((ptr_t)((((word) (&_etext)) + 0xfff) & ~0xfff)) +# endif +# include <features.h> +# if defined(__GLIBC__) && __GLIBC__ >= 2 + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# else + extern char **__environ; +# define DATASTART ((ptr_t)(&__environ)) + /* hideous kludge: __environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ + /* We could use _etext instead, but that */ + /* would include .rodata, which may */ + /* contain large read-only data tables */ + /* that we'd rather not scan. */ +# endif + extern int _end; +# define DATAEND (&_end) +# else + extern int etext; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0xfff) & ~0xfff)) +# endif +# endif +# ifdef CYGWIN32 +# define OS_TYPE "CYGWIN32" + extern int _data_start__; + extern int _data_end__; + extern int _bss_start__; + extern int _bss_end__; + /* For binutils 2.9.1, we have */ + /* DATASTART = _data_start__ */ + /* DATAEND = _bss_end__ */ + /* whereas for some earlier versions it was */ + /* DATASTART = _bss_start__ */ + /* DATAEND = _data_end__ */ + /* To get it right for both, we take the */ + /* minumum/maximum of the two. */ +# define MAX(x,y) ((x) > (y) ? (x) : (y)) +# define MIN(x,y) ((x) < (y) ? (x) : (y)) +# define DATASTART ((ptr_t) MIN(&_data_start__, &_bss_start__)) +# define DATAEND ((ptr_t) MAX(&_data_end__, &_bss_end__)) +# undef STACK_GRAN +# define STACK_GRAN 0x10000 +# define HEURISTIC1 +# endif +# ifdef OS2 +# define OS_TYPE "OS2" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. OS2 actually has the right */ + /* system call! */ +# define DATAEND /* not needed */ +# endif +# ifdef MSWIN32 +# define OS_TYPE "MSWIN32" + /* STACKBOTTOM and DATASTART are handled specially in */ + /* os_dep.c. */ +# ifndef __WATCOMC__ +# define MPROTECT_VDB +# endif +# define DATAEND /* not needed */ +# endif +# ifdef DJGPP +# define OS_TYPE "DJGPP" +# include "stubinfo.h" + extern int etext; + extern int _stklen; + extern int __djgpp_stack_limit; +# define DATASTART ((ptr_t)((((word) (&etext)) + 0x1ff) & ~0x1ff)) +/* # define STACKBOTTOM ((ptr_t)((word) _stubinfo + _stubinfo->size \ + + _stklen)) */ +# define STACKBOTTOM ((ptr_t)((word) __djgpp_stack_limit + _stklen)) + /* This may not be right. */ +# endif +# ifdef OPENBSD +# define OS_TYPE "OPENBSD" +# endif +# ifdef FREEBSD +# define OS_TYPE "FREEBSD" +# define MPROTECT_VDB +# endif +# ifdef NETBSD +# define OS_TYPE "NETBSD" +# endif +# ifdef THREE86BSD +# define OS_TYPE "THREE86BSD" +# endif +# ifdef BSDI +# define OS_TYPE "BSDI" +# endif +# if defined(OPENBSD) || defined(FREEBSD) || defined(NETBSD) \ + || defined(THREE86BSD) || defined(BSDI) +# define HEURISTIC2 + extern char etext; +# define DATASTART ((ptr_t)(&etext)) +# endif +# ifdef NEXT +# define OS_TYPE "NEXT" +# define DATASTART ((ptr_t) get_etext()) +# define STACKBOTTOM ((ptr_t)0xc0000000) +# define DATAEND /* not needed */ +# endif +# ifdef DOS4GW +# define OS_TYPE "DOS4GW" + extern long __nullarea; + extern char _end; + extern char *_STACKTOP; + /* Depending on calling conventions Watcom C either precedes + or does not precedes with undescore names of C-variables. + Make sure startup code variables always have the same names. */ + #pragma aux __nullarea "*"; + #pragma aux _end "*"; +# define STACKBOTTOM ((ptr_t) _STACKTOP) + /* confused? me too. */ +# define DATASTART ((ptr_t) &__nullarea) +# define DATAEND ((ptr_t) &_end) +# endif +# endif + +# ifdef NS32K +# define MACH_TYPE "NS32K" +# define ALIGNMENT 4 + extern char **environ; +# define DATASTART ((ptr_t)(&environ)) + /* hideous kludge: environ is the first */ + /* word in crt0.o, and delimits the start */ + /* of the data segment, no matter which */ + /* ld options were passed through. */ +# define STACKBOTTOM ((ptr_t) 0xfffff000) /* for Encore */ +# endif + +# ifdef MIPS +# define MACH_TYPE "MIPS" +# ifndef IRIX5 +# define DATASTART (ptr_t)0x10000000 + /* Could probably be slightly higher since */ + /* startup code allocates lots of stuff. */ +# else + extern int _fdata; +# define DATASTART ((ptr_t)(&_fdata)) +# ifdef USE_MMAP +# define HEAP_START (ptr_t)0x30000000 +# else +# define HEAP_START DATASTART +# endif + /* Lowest plausible heap address. */ + /* In the MMAP case, we map there. */ + /* In either case it is used to identify */ + /* heap sections so they're not */ + /* considered as roots. */ +# endif /* IRIX5 */ +# define HEURISTIC2 +/* # define STACKBOTTOM ((ptr_t)0x7fff8000) sometimes also works. */ +# ifdef ULTRIX +# define OS_TYPE "ULTRIX" +# define ALIGNMENT 4 +# endif +# ifdef RISCOS +# define OS_TYPE "RISCOS" +# define ALIGNMENT 4 /* Required by hardware */ +# endif +# ifdef IRIX5 +# define OS_TYPE "IRIX5" +# define MPROTECT_VDB +# ifdef _MIPS_SZPTR +# define CPP_WORDSZ _MIPS_SZPTR +# define ALIGNMENT (_MIPS_SZPTR/8) +# if CPP_WORDSZ != 64 +# define ALIGN_DOUBLE +# endif +# else +# define ALIGNMENT 4 +# define ALIGN_DOUBLE +# endif +# define DYNAMIC_LOADING +# endif +# endif + +# ifdef RS6000 +# define MACH_TYPE "RS6000" +# define ALIGNMENT 4 +# define DATASTART ((ptr_t)0x20000000) + extern int errno; +# define STACKBOTTOM ((ptr_t)((ulong)&errno)) +# define DYNAMIC_LOADING + /* For really old versions of AIX, this may have to be removed. */ +# endif + +# ifdef HP_PA +# define MACH_TYPE "HP_PA" +# define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int __data_start; +# define DATASTART ((ptr_t)(&__data_start)) +# if 0 + /* The following appears to work for 7xx systems running HP/UX */ + /* 9.xx Furthermore, it might result in much faster */ + /* collections than HEURISTIC2, which may involve scanning */ + /* segments that directly precede the stack. It is not the */ + /* default, since it may not work on older machine/OS */ + /* combinations. (Thanks to Raymond X.T. Nijssen for uncovering */ + /* this.) */ +# define STACKBOTTOM ((ptr_t) 0x7b033000) /* from /etc/conf/h/param.h */ +# else +# define HEURISTIC2 +# endif +# define STACK_GROWS_UP +# define DYNAMIC_LOADING +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGE_SIZE) + /* They misspelled the Posix macro? */ +# endif + +# ifdef ALPHA +# define MACH_TYPE "ALPHA" +# define ALIGNMENT 8 +# ifdef OSF1 +# define OS_TYPE "OSF1" +# define DATASTART ((ptr_t) 0x140000000) + extern _end; +# define DATAEND ((ptr_t) &_end) +# define HEURISTIC2 + /* Normally HEURISTIC2 is too conervative, since */ + /* the text segment immediately follows the stack. */ + /* Hence we give an upper pound. */ + extern int __start; +# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) +# define CPP_WORDSZ 64 +# define MPROTECT_VDB +# define DYNAMIC_LOADING +# endif +# ifdef LINUX +# define OS_TYPE "LINUX" +# define CPP_WORDSZ 64 +# define STACKBOTTOM ((ptr_t) 0x120000000) +# ifdef __ELF__ +# if 0 + /* __data_start apparently disappeared in some recent releases. */ + extern int __data_start; +# define DATASTART &__data_start +# endif +# define DATASTART GC_data_start +# define DYNAMIC_LOADING +# else +# define DATASTART ((ptr_t) 0x140000000) +# endif + extern int _end; +# define DATAEND (&_end) +# define MPROTECT_VDB + /* Has only been superficially tested. May not */ + /* work on all versions. */ +# endif +# endif + +# ifdef M88K +# define MACH_TYPE "M88K" +# define ALIGNMENT 4 +# define ALIGN_DOUBLE + extern int etext; +# ifdef CX_UX +# define OS_TYPE "CX_UX" +# define DATASTART ((((word)&etext + 0x3fffff) & ~0x3fffff) + 0x10000) +# endif +# ifdef DGUX +# define OS_TYPE "DGUX" + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &etext) +# endif +# define STACKBOTTOM ((char*)0xf0000000) /* determined empirically */ +# endif + +# ifdef S370 +# define MACH_TYPE "S370" +# define OS_TYPE "UTS4" +# define ALIGNMENT 4 /* Required by hardware */ + extern int etext; + extern int _etext; + extern int _end; + extern char * GC_SysVGetDataStart(); +# define DATASTART (ptr_t)GC_SysVGetDataStart(0x10000, &_etext) +# define DATAEND (&_end) +# define HEURISTIC2 +# endif + +# ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +# endif + +# ifndef CPP_WORDSZ +# define CPP_WORDSZ 32 +# endif + +# ifndef OS_TYPE +# define OS_TYPE "" +# endif + +# ifndef DATAEND + extern int end; +# define DATAEND (&end) +# endif + +# if defined(SVR4) && !defined(GETPAGESIZE) +# include <unistd.h> +# define GETPAGESIZE() sysconf(_SC_PAGESIZE) +# endif + +# ifndef GETPAGESIZE +# if defined(SUNOS5) || defined(IRIX5) +# include <unistd.h> +# endif +# define GETPAGESIZE() getpagesize() +# endif + +# if defined(SUNOS5) || defined(DRSNX) || defined(UTS4) + /* OS has SVR4 generic features. Probably others also qualify. */ +# define SVR4 +# endif + +# if defined(SUNOS5) || defined(DRSNX) + /* OS has SUNOS5 style semi-undocumented interface to dynamic */ + /* loader. */ +# define SUNOS5DL + /* OS has SUNOS5 style signal handlers. */ +# define SUNOS5SIGS +# endif + +# if CPP_WORDSZ != 32 && CPP_WORDSZ != 64 + -> bad word size +# endif + +# ifdef PCR +# undef DYNAMIC_LOADING +# undef STACKBOTTOM +# undef HEURISTIC1 +# undef HEURISTIC2 +# undef PROC_VDB +# undef MPROTECT_VDB +# define PCR_VDB +# endif + +# ifdef SRC_M3 +/* Postponed for now. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef SMALL_CONFIG +/* Presumably not worth the space it takes. */ +# undef PROC_VDB +# undef MPROTECT_VDB +# endif + +# ifdef USE_MUNMAP +# undef MPROTECT_VDB /* Can't deal with address space holes. */ +# endif + +# if !defined(PCR_VDB) && !defined(PROC_VDB) && !defined(MPROTECT_VDB) +# define DEFAULT_VDB +# endif + +# if defined(_SOLARIS_PTHREADS) && !defined(SOLARIS_THREADS) +# define SOLARIS_THREADS +# endif +# if defined(IRIX_THREADS) && !defined(IRIX5) +--> inconsistent configuration +# endif +# if defined(IRIX_JDK_THREADS) && !defined(IRIX5) +--> inconsistent configuration +# endif +# if defined(LINUX_THREADS) && !defined(LINUX) +--> inconsistent configuration +# endif +# if defined(SOLARIS_THREADS) && !defined(SUNOS5) +--> inconsistent configuration +# endif +# if defined(PCR) || defined(SRC_M3) || \ + defined(SOLARIS_THREADS) || defined(WIN32_THREADS) || \ + defined(IRIX_THREADS) || defined(LINUX_THREADS) || \ + defined(IRIX_JDK_THREADS) +# define THREADS +# endif + +# if defined(HP_PA) || defined(M88K) || defined(POWERPC) \ + || (defined(I386) && defined(OS2)) || defined(UTS4) || defined(LINT) + /* Use setjmp based hack to mark from callee-save registers. */ +# define USE_GENERIC_PUSH_REGS +# endif +# if defined(SPARC) && !defined(LINUX) +# define SAVE_CALL_CHAIN +# define ASM_CLEAR_CODE /* Stack clearing is crucial, and we */ + /* include assembly code to do it well. */ +# endif + +# endif diff --git a/gc/include/weakpointer.h b/gc/include/weakpointer.h new file mode 100644 index 0000000..84906b0 --- /dev/null +++ b/gc/include/weakpointer.h @@ -0,0 +1,221 @@ +#ifndef _weakpointer_h_ +#define _weakpointer_h_ + +/**************************************************************************** + +WeakPointer and CleanUp + + Copyright (c) 1991 by Xerox Corporation. All rights reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to copy this code for any purpose, + provided the above notices are retained on all copies. + + Last modified on Mon Jul 17 18:16:01 PDT 1995 by ellis + +****************************************************************************/ + +/**************************************************************************** + +WeakPointer + +A weak pointer is a pointer to a heap-allocated object that doesn't +prevent the object from being garbage collected. Weak pointers can be +used to track which objects haven't yet been reclaimed by the +collector. A weak pointer is deactivated when the collector discovers +its referent object is unreachable by normal pointers (reachability +and deactivation are defined more precisely below). A deactivated weak +pointer remains deactivated forever. + +****************************************************************************/ + + +template< class T > class WeakPointer { +public: + +WeakPointer( T* t = 0 ) + /* Constructs a weak pointer for *t. t may be null. It is an error + if t is non-null and *t is not a collected object. */ + {impl = _WeakPointer_New( t );} + +T* Pointer() + /* wp.Pointer() returns a pointer to the referent object of wp or + null if wp has been deactivated (because its referent object + has been discovered unreachable by the collector). */ + {return (T*) _WeakPointer_Pointer( this->impl );} + +int operator==( WeakPointer< T > wp2 ) + /* Given weak pointers wp1 and wp2, if wp1 == wp2, then wp1 and + wp2 refer to the same object. If wp1 != wp2, then either wp1 + and wp2 don't refer to the same object, or if they do, one or + both of them has been deactivated. (Note: If objects t1 and t2 + are never made reachable by their clean-up functions, then + WeakPointer<T>(t1) == WeakPointer<T>(t2) if and only t1 == t2.) */ + {return _WeakPointer_Equal( this->impl, wp2.impl );} + +int Hash() + /* Returns a hash code suitable for use by multiplicative- and + division-based hash tables. If wp1 == wp2, then wp1.Hash() == + wp2.Hash(). */ + {return _WeakPointer_Hash( this->impl );} + +private: +void* impl; +}; + +/***************************************************************************** + +CleanUp + +A garbage-collected object can have an associated clean-up function +that will be invoked some time after the collector discovers the +object is unreachable via normal pointers. Clean-up functions can be +used to release resources such as open-file handles or window handles +when their containing objects become unreachable. If a C++ object has +a non-empty explicit destructor (i.e. it contains programmer-written +code), the destructor will be automatically registered as the object's +initial clean-up function. + +There is no guarantee that the collector will detect every unreachable +object (though it will find almost all of them). Clients should not +rely on clean-up to cause some action to occur immediately -- clean-up +is only a mechanism for improving resource usage. + +Every object with a clean-up function also has a clean-up queue. When +the collector finds the object is unreachable, it enqueues it on its +queue. The clean-up function is applied when the object is removed +from the queue. By default, objects are enqueued on the garbage +collector's queue, and the collector removes all objects from its +queue after each collection. If a client supplies another queue for +objects, it is his responsibility to remove objects (and cause their +functions to be called) by polling it periodically. + +Clean-up queues allow clean-up functions accessing global data to +synchronize with the main program. Garbage collection can occur at any +time, and clean-ups invoked by the collector might access data in an +inconsistent state. A client can control this by defining an explicit +queue for objects and polling it at safe points. + +The following definitions are used by the specification below: + +Given a pointer t to a collected object, the base object BO(t) is the +value returned by new when it created the object. (Because of multiple +inheritance, t and BO(t) may not be the same address.) + +A weak pointer wp references an object *t if BO(wp.Pointer()) == +BO(t). + +***************************************************************************/ + +template< class T, class Data > class CleanUp { +public: + +static void Set( T* t, void c( Data* d, T* t ), Data* d = 0 ) + /* Sets the clean-up function of object BO(t) to be <c, d>, + replacing any previously defined clean-up function for BO(t); c + and d can be null, but t cannot. Sets the clean-up queue for + BO(t) to be the collector's queue. When t is removed from its + clean-up queue, its clean-up will be applied by calling c(d, + t). It is an error if *t is not a collected object. */ + {_CleanUp_Set( t, c, d );} + +static void Call( T* t ) + /* Sets the new clean-up function for BO(t) to be null and, if the + old one is non-null, calls it immediately, even if BO(t) is + still reachable. Deactivates any weak pointers to BO(t). */ + {_CleanUp_Call( t );} + +class Queue {public: + Queue() + /* Constructs a new queue. */ + {this->head = _CleanUp_Queue_NewHead();} + + void Set( T* t ) + /* q.Set(t) sets the clean-up queue of BO(t) to be q. */ + {_CleanUp_Queue_Set( this->head, t );} + + int Call() + /* If q is non-empty, q.Call() removes the first object and + calls its clean-up function; does nothing if q is + empty. Returns true if there are more objects in the + queue. */ + {return _CleanUp_Queue_Call( this->head );} + + private: + void* head; + }; +}; + +/********************************************************************** + +Reachability and Clean-up + +An object O is reachable if it can be reached via a non-empty path of +normal pointers from the registers, stacks, global variables, or an +object with a non-null clean-up function (including O itself), +ignoring pointers from an object to itself. + +This definition of reachability ensures that if object B is accessible +from object A (and not vice versa) and if both A and B have clean-up +functions, then A will always be cleaned up before B. Note that as +long as an object with a clean-up function is contained in a cycle of +pointers, it will always be reachable and will never be cleaned up or +collected. + +When the collector finds an unreachable object with a null clean-up +function, it atomically deactivates all weak pointers referencing the +object and recycles its storage. If object B is accessible from object +A via a path of normal pointers, A will be discovered unreachable no +later than B, and a weak pointer to A will be deactivated no later +than a weak pointer to B. + +When the collector finds an unreachable object with a non-null +clean-up function, the collector atomically deactivates all weak +pointers referencing the object, redefines its clean-up function to be +null, and enqueues it on its clean-up queue. The object then becomes +reachable again and remains reachable at least until its clean-up +function executes. + +The clean-up function is assured that its argument is the only +accessible pointer to the object. Nothing prevents the function from +redefining the object's clean-up function or making the object +reachable again (for example, by storing the pointer in a global +variable). + +If the clean-up function does not make its object reachable again and +does not redefine its clean-up function, then the object will be +collected by a subsequent collection (because the object remains +unreachable and now has a null clean-up function). If the clean-up +function does make its object reachable again and a clean-up function +is subsequently redefined for the object, then the new clean-up +function will be invoked the next time the collector finds the object +unreachable. + +Note that a destructor for a collected object cannot safely redefine a +clean-up function for its object, since after the destructor executes, +the object has been destroyed into "raw memory". (In most +implementations, destroying an object mutates its vtbl.) + +Finally, note that calling delete t on a collected object first +deactivates any weak pointers to t and then invokes its clean-up +function (destructor). + +**********************************************************************/ + +extern "C" { + void* _WeakPointer_New( void* t ); + void* _WeakPointer_Pointer( void* wp ); + int _WeakPointer_Equal( void* wp1, void* wp2 ); + int _WeakPointer_Hash( void* wp ); + void _CleanUp_Set( void* t, void (*c)( void* d, void* t ), void* d ); + void _CleanUp_Call( void* t ); + void* _CleanUp_Queue_NewHead (); + void _CleanUp_Queue_Set( void* h, void* t ); + int _CleanUp_Queue_Call( void* h ); +} + +#endif /* _weakpointer_h_ */ + + diff --git a/gc/irix_threads.c b/gc/irix_threads.c new file mode 100644 index 0000000..5efca21 --- /dev/null +++ b/gc/irix_threads.c @@ -0,0 +1,674 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Support code for Irix (>=6.2) Pthreads. This relies on properties + * not guaranteed by the Pthread standard. It may or may not be portable + * to other implementations. + * + * Note that there is a lot of code duplication between linux_threads.c + * and irix_threads.c; any changes made here may need to be reflected + * there too. + */ + +# if defined(IRIX_THREADS) + +# include "gc_priv.h" +# include <pthread.h> +# include <semaphore.h> +# include <time.h> +# include <errno.h> +# include <unistd.h> +# include <sys/mman.h> +# include <sys/time.h> + +#undef pthread_create +#undef pthread_sigmask +#undef pthread_join + +void GC_thr_init(); + +#if 0 +void GC_print_sig_mask() +{ + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask"); + GC_printf0("Blocked: "); + for (i = 1; i <= MAXSIG; i++) { + if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } + } + GC_printf0("\n"); +} +#endif + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. We never actually create detached threads. We allocate all */ +/* new thread stacks ourselves. These allow us to maintain this */ +/* data structure. */ +/* Protected by GC_thr_lock. */ +/* Some of this should be declared volatile, but that's incosnsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; + word stop; +# define NOT_STOPPED 0 +# define PLEASE_STOP 1 +# define STOPPED 2 + word flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define CLIENT_OWNS_STACK 4 + /* Stack was supplied by client. */ + ptr_t stack; + ptr_t stack_ptr; /* Valid only when stopped. */ + /* But must be within stack region at */ + /* all times. */ + size_t stack_size; /* 0 for original thread. */ + void * status; /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ +} * GC_thread; + +GC_thread GC_lookup_thread(pthread_t id); + +/* + * The only way to suspend threads given the pthread interface is to send + * signals. Unfortunately, this means we have to reserve + * a signal, and intercept client calls to change the signal mask. + */ +# define SIG_SUSPEND (SIGRTMIN + 6) + +pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER; + /* Number of threads stopped so far */ +pthread_cond_t GC_suspend_ack_cv = PTHREAD_COND_INITIALIZER; +pthread_cond_t GC_continue_cv = PTHREAD_COND_INITIALIZER; + +void GC_suspend_handler(int sig) +{ + int dummy; + GC_thread me; + sigset_t all_sigs; + sigset_t old_sigs; + int i; + + if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); + me = GC_lookup_thread(pthread_self()); + /* The lookup here is safe, since I'm doing this on behalf */ + /* of a thread which holds the allocation lock in order */ + /* to stop the world. Thus concurrent modification of the */ + /* data structure is impossible. */ + if (PLEASE_STOP != me -> stop) { + /* Misdirected signal. */ + pthread_mutex_unlock(&GC_suspend_lock); + return; + } + pthread_mutex_lock(&GC_suspend_lock); + me -> stack_ptr = (ptr_t)(&dummy); + me -> stop = STOPPED; + pthread_cond_signal(&GC_suspend_ack_cv); + pthread_cond_wait(&GC_continue_cv, &GC_suspend_lock); + pthread_mutex_unlock(&GC_suspend_lock); + /* GC_printf1("Continuing 0x%x\n", pthread_self()); */ +} + + +GC_bool GC_thr_initialized = FALSE; + +size_t GC_min_stack_sz; + +size_t GC_page_sz; + +# define N_FREE_LISTS 25 +ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 }; + /* GC_stack_free_lists[i] is free list for stacks of */ + /* size GC_min_stack_sz*2**i. */ + /* Free lists are linked through first word. */ + +/* Return a stack of size at least *stack_size. *stack_size is */ +/* replaced by the actual stack size. */ +/* Caller holds allocation lock. */ +ptr_t GC_stack_alloc(size_t * stack_size) +{ + register size_t requested_sz = *stack_size; + register size_t search_sz = GC_min_stack_sz; + register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */ + register ptr_t result; + + while (search_sz < requested_sz) { + search_sz *= 2; + index++; + } + if ((result = GC_stack_free_lists[index]) == 0 + && (result = GC_stack_free_lists[index+1]) != 0) { + /* Try next size up. */ + search_sz *= 2; index++; + } + if (result != 0) { + GC_stack_free_lists[index] = *(ptr_t *)result; + } else { + result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz); + result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1)); + /* Protect hottest page to detect overflow. */ + /* mprotect(result, GC_page_sz, PROT_NONE); */ + result += GC_page_sz; + } + *stack_size = search_sz; + return(result); +} + +/* Caller holds allocation lock. */ +void GC_stack_free(ptr_t stack, size_t size) +{ + register int index = 0; + register size_t search_sz = GC_min_stack_sz; + + while (search_sz < size) { + search_sz *= 2; + index++; + } + if (search_sz != size) ABORT("Bad stack size"); + *(ptr_t *)stack = GC_stack_free_lists[index]; + GC_stack_free_lists[index] = stack; +} + + + +# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ +volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +GC_thread GC_new_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + GC_thread result; + static struct GC_Thread_Rep first_thread; + static GC_bool first_thread_used = FALSE; + + if (!first_thread_used) { + result = &first_thread; + first_thread_used = TRUE; + /* Dont acquire allocation lock, since we may already hold it. */ + } else { + result = (struct GC_Thread_Rep *) + GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL); + } + if (result == 0) return(0); + result -> id = id; + result -> next = GC_threads[hv]; + GC_threads[hv] = result; + /* result -> flags = 0; */ + /* result -> stop = 0; */ + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* Caller holds allocation lock. */ +void GC_delete_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (!pthread_equal(p -> id, id)) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same pthread id. */ +/* This is OK, but we need a way to delete a specific one. */ +void GC_delete_gc_thread(pthread_t id, GC_thread gc_id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (p != gc_id) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* Return a GC_thread corresponding to a given thread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock or otherwise inhibits */ +/* updates. */ +/* If there is more than one thread with the given id we */ +/* return the most recent one. */ +GC_thread GC_lookup_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + + while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next; + return(p); +} + + +/* Caller holds allocation lock. */ +void GC_stop_world() +{ + pthread_t my_thread = pthread_self(); + register int i; + register GC_thread p; + register int result; + struct timespec timeout; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> id != my_thread) { + if (p -> flags & FINISHED) { + p -> stop = STOPPED; + continue; + } + p -> stop = PLEASE_STOP; + result = pthread_kill(p -> id, SIG_SUSPEND); + /* GC_printf1("Sent signal to 0x%x\n", p -> id); */ + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + p -> stop = STOPPED; + break; + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + } + } + } + pthread_mutex_lock(&GC_suspend_lock); + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + while (p -> id != my_thread && p -> stop != STOPPED) { + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_nsec += 50000000; /* 50 msecs */ + if (timeout.tv_nsec >= 1000000000) { + timeout.tv_nsec -= 1000000000; + ++timeout.tv_sec; + } + result = pthread_cond_timedwait(&GC_suspend_ack_cv, + &GC_suspend_lock, + &timeout); + if (result == ETIMEDOUT) { + /* Signal was lost or misdirected. Try again. */ + /* Duplicate signals should be benign. */ + result = pthread_kill(p -> id, SIG_SUSPEND); + } + } + } + } + pthread_mutex_unlock(&GC_suspend_lock); + /* GC_printf1("World stopped 0x%x\n", pthread_self()); */ +} + +/* Caller holds allocation lock. */ +void GC_start_world() +{ + GC_thread p; + unsigned i; + + /* GC_printf0("World starting\n"); */ + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + p -> stop = NOT_STOPPED; + } + } + pthread_mutex_lock(&GC_suspend_lock); + /* All other threads are at pthread_cond_wait in signal handler. */ + /* Otherwise we couldn't have acquired the lock. */ + pthread_mutex_unlock(&GC_suspend_lock); + pthread_cond_broadcast(&GC_continue_cv); +} + +# ifdef MMAP_STACKS +--> not really supported yet. +int GC_is_thread_stack(ptr_t addr) +{ + register int i; + register GC_thread p; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> stack_size != 0) { + if (p -> stack <= addr && + addr < p -> stack + p -> stack_size) + return 1; + } + } + } + return 0; +} +# endif + +/* We hold allocation lock. We assume the world is stopped. */ +void GC_push_all_stacks() +{ + register int i; + register GC_thread p; + register ptr_t sp = GC_approx_sp(); + register ptr_t lo, hi; + pthread_t me = pthread_self(); + + if (!GC_thr_initialized) GC_thr_init(); + /* GC_printf1("Pushing stacks from thread 0x%x\n", me); */ + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> flags & FINISHED) continue; + if (pthread_equal(p -> id, me)) { + lo = GC_approx_sp(); + } else { + lo = p -> stack_ptr; + } + if (p -> stack_size != 0) { + hi = p -> stack + p -> stack_size; + } else { + /* The original stack. */ + hi = GC_stackbottom; + } + GC_push_all_stack(lo, hi); + } + } +} + + +/* We hold the allocation lock. */ +void GC_thr_init() +{ + GC_thread t; + struct sigaction act; + + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + GC_min_stack_sz = HBLKSIZE; + GC_page_sz = sysconf(_SC_PAGESIZE); + (void) sigaction(SIG_SUSPEND, 0, &act); + if (act.sa_handler != SIG_DFL) + ABORT("Previously installed SIG_SUSPEND handler"); + /* Install handler. */ + act.sa_handler = GC_suspend_handler; + act.sa_flags = SA_RESTART; + (void) sigemptyset(&act.sa_mask); + if (0 != sigaction(SIG_SUSPEND, &act, 0)) + ABORT("Failed to install SIG_SUSPEND handler"); + /* Add the initial thread, so we can stop it. */ + t = GC_new_thread(pthread_self()); + t -> stack_size = 0; + t -> stack_ptr = (ptr_t)(&t); + t -> flags = DETACHED; +} + +int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + sigset_t fudged_set; + + if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { + fudged_set = *set; + sigdelset(&fudged_set, SIG_SUSPEND); + set = &fudged_set; + } + return(pthread_sigmask(how, set, oset)); +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; + word flags; + ptr_t stack; + size_t stack_size; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ +}; + +void GC_thread_exit_proc(void *arg) +{ + GC_thread me; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + if (me -> flags & DETACHED) { + GC_delete_thread(pthread_self()); + } else { + me -> flags |= FINISHED; + } + UNLOCK(); +} + +int GC_pthread_join(pthread_t thread, void **retval) +{ + int result; + GC_thread thread_gc_id; + + LOCK(); + thread_gc_id = GC_lookup_thread(thread); + /* This is guaranteed to be the intended one, since the thread id */ + /* cant have been recycled by pthreads. */ + UNLOCK(); + result = pthread_join(thread, retval); + /* Some versions of the Irix pthreads library can erroneously */ + /* return EINTR when the call succeeds. */ + if (EINTR == result) result = 0; + LOCK(); + /* Here the pthread thread id may have been recycled. */ + GC_delete_gc_thread(thread, thread_gc_id); + UNLOCK(); + return result; +} + +void * GC_start_routine(void * arg) +{ + struct start_info * si = arg; + void * result; + GC_thread me; + pthread_t my_pthread; + void *(*start)(void *); + void *start_arg; + + my_pthread = pthread_self(); + /* If a GC occurs before the thread is registered, that GC will */ + /* ignore this thread. That's fine, since it will block trying to */ + /* acquire the allocation lock, and won't yet hold interesting */ + /* pointers. */ + LOCK(); + /* We register the thread here instead of in the parent, so that */ + /* we don't need to hold the allocation lock during pthread_create. */ + /* Holding the allocation lock there would make REDIRECT_MALLOC */ + /* impossible. It probably still doesn't work, but we're a little */ + /* closer ... */ + /* This unfortunately means that we have to be careful the parent */ + /* doesn't try to do a pthread_join before we're registered. */ + me = GC_new_thread(my_pthread); + me -> flags = si -> flags; + me -> stack = si -> stack; + me -> stack_size = si -> stack_size; + me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word); + UNLOCK(); + start = si -> start_routine; + start_arg = si -> arg; + sem_post(&(si -> registered)); + pthread_cleanup_push(GC_thread_exit_proc, 0); + result = (*start)(start_arg); + me -> status = result; + me -> flags |= FINISHED; + pthread_cleanup_pop(1); + /* This involves acquiring the lock, ensuring that we can't exit */ + /* while a collection that thinks we're alive is trying to stop */ + /* us. */ + return(result); +} + +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + int result; + GC_thread t; + void * stack; + size_t stacksize; + pthread_attr_t new_attr; + int detachstate; + word my_flags = 0; + struct start_info * si = GC_malloc(sizeof(struct start_info)); + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + + if (0 == si) return(ENOMEM); + sem_init(&(si -> registered), 0, 0); + si -> start_routine = start_routine; + si -> arg = arg; + LOCK(); + if (!GC_thr_initialized) GC_thr_init(); + if (NULL == attr) { + stack = 0; + (void) pthread_attr_init(&new_attr); + } else { + new_attr = *attr; + pthread_attr_getstackaddr(&new_attr, &stack); + } + pthread_attr_getstacksize(&new_attr, &stacksize); + pthread_attr_getdetachstate(&new_attr, &detachstate); + if (stacksize < GC_min_stack_sz) ABORT("Stack too small"); + if (0 == stack) { + stack = (void *)GC_stack_alloc(&stacksize); + if (0 == stack) { + UNLOCK(); + return(ENOMEM); + } + pthread_attr_setstackaddr(&new_attr, stack); + } else { + my_flags |= CLIENT_OWNS_STACK; + } + if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; + si -> flags = my_flags; + si -> stack = stack; + si -> stack_size = stacksize; + result = pthread_create(new_thread, &new_attr, GC_start_routine, si); + if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) { + GC_stack_free(stack, stacksize); + } + UNLOCK(); + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto si until the child is done */ + /* with it. Thus it doesn't matter whether it is otherwise */ + /* visible to the collector. */ + if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed"); + sem_destroy(&(si -> registered)); + /* pthread_attr_destroy(&new_attr); */ + return(result); +} + +GC_bool GC_collecting = 0; /* A hint that we're in the collector and */ + /* holding the allocation lock for an */ + /* extended period. */ + +/* Reasonably fast spin locks. Basically the same implementation */ +/* as STL alloc.h. This isn't really the right way to do this. */ +/* but until the POSIX scheduling mess gets straightened out ... */ + +unsigned long GC_allocate_lock = 0; + +#define SLEEP_THRESHOLD 3 + +void GC_lock() +{ +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max 1000 /* spin cycles for multiprocessor */ + static unsigned spin_max = low_spin_max; + unsigned my_spin_max; + static unsigned last_spins = 0; + unsigned my_last_spins; + volatile unsigned junk; +# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk + int i; + + if (!GC_test_and_set(&GC_allocate_lock, 1)) { + return; + } + junk = 0; + my_spin_max = spin_max; + my_last_spins = last_spins; + for (i = 0; i < my_spin_max; i++) { + if (GC_collecting) goto yield; + if (i < my_last_spins/2 || GC_allocate_lock) { + PAUSE; + continue; + } + if (!GC_test_and_set(&GC_allocate_lock, 1)) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + last_spins = i; + spin_max = high_spin_max; + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + spin_max = low_spin_max; +yield: + for (i = 0;; ++i) { + if (!GC_test_and_set(&GC_allocate_lock, 1)) { + return; + } + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 26) i = 26; + /* Don't wait for more than about 60msecs, even */ + /* under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } + } +} + + + +# else + +#ifndef LINT + int GC_no_Irix_threads; +#endif + +# endif /* IRIX_THREADS */ + diff --git a/gc/linux_threads.c b/gc/linux_threads.c new file mode 100644 index 0000000..8287dce --- /dev/null +++ b/gc/linux_threads.c @@ -0,0 +1,665 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Support code for LinuxThreads, the clone()-based kernel + * thread package for Linux which is included in libc6. + * + * This code relies on implementation details of LinuxThreads, + * (i.e. properties not guaranteed by the Pthread standard): + * + * - the function GC_linux_thread_top_of_stack(void) + * relies on the way LinuxThreads lays out thread stacks + * in the address space. + * + * Note that there is a lot of code duplication between linux_threads.c + * and irix_threads.c; any changes made here may need to be reflected + * there too. + */ + +/* #define DEBUG_THREADS 1 */ + +/* ANSI C requires that a compilation unit contains something */ +# include "gc_priv.h" + +# if defined(LINUX_THREADS) + +# include <pthread.h> +# include <time.h> +# include <errno.h> +# include <unistd.h> +# include <sys/mman.h> +# include <sys/time.h> +# include <semaphore.h> + +#undef pthread_create +#undef pthread_sigmask +#undef pthread_join + +void GC_thr_init(); + +#if 0 +void GC_print_sig_mask() +{ + sigset_t blocked; + int i; + + if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) + ABORT("pthread_sigmask"); + GC_printf0("Blocked: "); + for (i = 1; i <= MAXSIG; i++) { + if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } + } + GC_printf0("\n"); +} +#endif + +/* We use the allocation lock to protect thread-related data structures. */ + +/* The set of all known threads. We intercept thread creation and */ +/* joins. We never actually create detached threads. We allocate all */ +/* new thread stacks ourselves. These allow us to maintain this */ +/* data structure. */ +/* Protected by GC_thr_lock. */ +/* Some of this should be declared volatile, but that's incosnsistent */ +/* with some library routine declarations. */ +typedef struct GC_Thread_Rep { + struct GC_Thread_Rep * next; /* More recently allocated threads */ + /* with a given pthread id come */ + /* first. (All but the first are */ + /* guaranteed to be dead, but we may */ + /* not yet have registered the join.) */ + pthread_t id; + word flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define MAIN_THREAD 4 /* True for the original thread only. */ + + ptr_t stack_end; + ptr_t stack_ptr; /* Valid only when stopped. */ + int signal; + void * status; /* The value returned from the thread. */ + /* Used only to avoid premature */ + /* reclamation of any data it might */ + /* reference. */ +} * GC_thread; + +GC_thread GC_lookup_thread(pthread_t id); + +/* + * The only way to suspend threads given the pthread interface is to send + * signals. We can't use SIGSTOP directly, because we need to get the + * thread to save its stack pointer in the GC thread table before + * suspending. So we have to reserve a signal of our own for this. + * This means we have to intercept client calls to change the signal mask. + * The linuxthreads package already uses SIGUSR1 and SIGUSR2, + * so we need to reuse something else. I chose SIGPWR. + * (Perhaps SIGUNUSED would be a better choice.) + */ +#define SIG_SUSPEND SIGPWR + +#define SIG_RESTART SIGXCPU + +sem_t GC_suspend_ack_sem; + +/* +GC_linux_thread_top_of_stack() relies on implementation details of +LinuxThreads, namely that thread stacks are allocated on 2M boundaries +and grow to no more than 2M. +To make sure that we're using LinuxThreads and not some other thread +package, we generate a dummy reference to `pthread_kill_other_threads_np' +(was `__pthread_initial_thread_bos' but that disappeared), +which is a symbol defined in LinuxThreads, but (hopefully) not in other +thread packages. +*/ +void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np; + +#define LINUX_THREADS_STACK_SIZE (2 * 1024 * 1024) + +static inline ptr_t GC_linux_thread_top_of_stack(void) +{ + char *sp = GC_approx_sp(); + ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1); +#if DEBUG_THREADS + GC_printf1("SP = %lx\n", (unsigned long)sp); + GC_printf1("TOS = %lx\n", (unsigned long)tos); +#endif + return tos; +} + +void GC_suspend_handler(int sig) +{ + int dummy; + pthread_t my_thread = pthread_self(); + GC_thread me; + sigset_t all_sigs; + sigset_t old_sigs; + int i; + sigset_t mask; + + if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler"); + +#if DEBUG_THREADS + GC_printf1("Suspending 0x%x\n", my_thread); +#endif + + me = GC_lookup_thread(my_thread); + /* The lookup here is safe, since I'm doing this on behalf */ + /* of a thread which holds the allocation lock in order */ + /* to stop the world. Thus concurrent modification of the */ + /* data structure is impossible. */ + me -> stack_ptr = (ptr_t)(&dummy); + me -> stack_end = GC_linux_thread_top_of_stack(); + + /* Tell the thread that wants to stop the world that this */ + /* thread has been stopped. Note that sem_post() is */ + /* the only async-signal-safe primitive in LinuxThreads. */ + sem_post(&GC_suspend_ack_sem); + + /* Wait until that thread tells us to restart by sending */ + /* this thread a SIG_RESTART signal. */ + /* SIG_RESTART should be masked at this point. Thus there */ + /* is no race. */ + if (sigfillset(&mask) != 0) ABORT("sigfillset() failed"); + if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed"); + do { + me->signal = 0; + sigsuspend(&mask); /* Wait for signal */ + } while (me->signal != SIG_RESTART); + +#if DEBUG_THREADS + GC_printf1("Continuing 0x%x\n", my_thread); +#endif +} + +void GC_restart_handler(int sig) +{ + GC_thread me; + + if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler"); + + /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */ + /* The lookup here is safe, since I'm doing this on behalf */ + /* of a thread which holds the allocation lock in order */ + /* to stop the world. Thus concurrent modification of the */ + /* data structure is impossible. */ + me = GC_lookup_thread(pthread_self()); + me->signal = SIG_RESTART; + + /* + ** Note: even if we didn't do anything useful here, + ** it would still be necessary to have a signal handler, + ** rather than ignoring the signals, otherwise + ** the signals will not be delivered at all, and + ** will thus not interrupt the sigsuspend() above. + */ + +#if DEBUG_THREADS + GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self()); +#endif +} + +GC_bool GC_thr_initialized = FALSE; + +# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ +volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +GC_thread GC_new_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + GC_thread result; + static struct GC_Thread_Rep first_thread; + static GC_bool first_thread_used = FALSE; + + if (!first_thread_used) { + result = &first_thread; + first_thread_used = TRUE; + /* Dont acquire allocation lock, since we may already hold it. */ + } else { + result = (struct GC_Thread_Rep *) + GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL); + } + if (result == 0) return(0); + result -> id = id; + result -> next = GC_threads[hv]; + GC_threads[hv] = result; + /* result -> flags = 0; */ + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* Caller holds allocation lock. */ +void GC_delete_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (!pthread_equal(p -> id, id)) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* If a thread has been joined, but we have not yet */ +/* been notified, then there may be more than one thread */ +/* in the table with the same pthread id. */ +/* This is OK, but we need a way to delete a specific one. */ +void GC_delete_gc_thread(pthread_t id, GC_thread gc_id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (p != gc_id) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* Return a GC_thread corresponding to a given thread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock or otherwise inhibits */ +/* updates. */ +/* If there is more than one thread with the given id we */ +/* return the most recent one. */ +GC_thread GC_lookup_thread(pthread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + + while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next; + return(p); +} + +/* Caller holds allocation lock. */ +void GC_stop_world() +{ + pthread_t my_thread = pthread_self(); + register int i; + register GC_thread p; + register int n_live_threads = 0; + register int result; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> id != my_thread) { + if (p -> flags & FINISHED) continue; + n_live_threads++; + #if DEBUG_THREADS + GC_printf1("Sending suspend signal to 0x%x\n", p -> id); + #endif + result = pthread_kill(p -> id, SIG_SUSPEND); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + } + } + } + for (i = 0; i < n_live_threads; i++) { + sem_wait(&GC_suspend_ack_sem); + } + #if DEBUG_THREADS + GC_printf1("World stopped 0x%x\n", pthread_self()); + #endif +} + +/* Caller holds allocation lock. */ +void GC_start_world() +{ + pthread_t my_thread = pthread_self(); + register int i; + register GC_thread p; + register int n_live_threads = 0; + register int result; + +# if DEBUG_THREADS + GC_printf0("World starting\n"); +# endif + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> id != my_thread) { + if (p -> flags & FINISHED) continue; + n_live_threads++; + #if DEBUG_THREADS + GC_printf1("Sending restart signal to 0x%x\n", p -> id); + #endif + result = pthread_kill(p -> id, SIG_RESTART); + switch(result) { + case ESRCH: + /* Not really there anymore. Possible? */ + n_live_threads--; + break; + case 0: + break; + default: + ABORT("pthread_kill failed"); + } + } + } + } + #if DEBUG_THREADS + GC_printf0("World started\n"); + #endif +} + +/* We hold allocation lock. We assume the world is stopped. */ +void GC_push_all_stacks() +{ + register int i; + register GC_thread p; + register ptr_t sp = GC_approx_sp(); + register ptr_t lo, hi; + pthread_t me = pthread_self(); + + if (!GC_thr_initialized) GC_thr_init(); + #if DEBUG_THREADS + GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me); + #endif + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> flags & FINISHED) continue; + if (pthread_equal(p -> id, me)) { + lo = GC_approx_sp(); + } else { + lo = p -> stack_ptr; + } + if ((p -> flags & MAIN_THREAD) == 0) { + if (pthread_equal(p -> id, me)) { + hi = GC_linux_thread_top_of_stack(); + } else { + hi = p -> stack_end; + } + } else { + /* The original stack. */ + hi = GC_stackbottom; + } + #if DEBUG_THREADS + GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n", + (unsigned long) p -> id, + (unsigned long) lo, (unsigned long) hi); + #endif + GC_push_all_stack(lo, hi); + } + } +} + + +/* We hold the allocation lock. */ +void GC_thr_init() +{ + GC_thread t; + struct sigaction act; + + if (GC_thr_initialized) return; + GC_thr_initialized = TRUE; + + if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0) + ABORT("sem_init failed"); + + act.sa_flags = SA_RESTART; + if (sigfillset(&act.sa_mask) != 0) { + ABORT("sigfillset() failed"); + } + /* SIG_RESTART is unmasked by the handler when necessary. */ + act.sa_handler = GC_suspend_handler; + if (sigaction(SIG_SUSPEND, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + + act.sa_handler = GC_restart_handler; + if (sigaction(SIG_RESTART, &act, NULL) != 0) { + ABORT("Cannot set SIG_SUSPEND handler"); + } + + /* Add the initial thread, so we can stop it. */ + t = GC_new_thread(pthread_self()); + t -> stack_ptr = 0; + t -> flags = DETACHED | MAIN_THREAD; +} + +int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) +{ + sigset_t fudged_set; + + if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) { + fudged_set = *set; + sigdelset(&fudged_set, SIG_SUSPEND); + set = &fudged_set; + } + return(pthread_sigmask(how, set, oset)); +} + +struct start_info { + void *(*start_routine)(void *); + void *arg; + word flags; + sem_t registered; /* 1 ==> in our thread table, but */ + /* parent hasn't yet noticed. */ +}; + + +void GC_thread_exit_proc(void *arg) +{ + GC_thread me; + struct start_info * si = arg; + + LOCK(); + me = GC_lookup_thread(pthread_self()); + if (me -> flags & DETACHED) { + GC_delete_thread(pthread_self()); + } else { + me -> flags |= FINISHED; + } + UNLOCK(); +} + +int GC_pthread_join(pthread_t thread, void **retval) +{ + int result; + GC_thread thread_gc_id; + + LOCK(); + thread_gc_id = GC_lookup_thread(thread); + /* This is guaranteed to be the intended one, since the thread id */ + /* cant have been recycled by pthreads. */ + UNLOCK(); + result = pthread_join(thread, retval); + LOCK(); + /* Here the pthread thread id may have been recycled. */ + GC_delete_gc_thread(thread, thread_gc_id); + UNLOCK(); + return result; +} + +void * GC_start_routine(void * arg) +{ + struct start_info * si = arg; + void * result; + GC_thread me; + pthread_t my_pthread; + void *(*start)(void *); + void *start_arg; + + my_pthread = pthread_self(); + LOCK(); + me = GC_new_thread(my_pthread); + me -> flags = si -> flags; + me -> stack_ptr = 0; + me -> stack_end = 0; + UNLOCK(); + start = si -> start_routine; + start_arg = si -> arg; + sem_post(&(si -> registered)); + pthread_cleanup_push(GC_thread_exit_proc, si); +# ifdef DEBUG_THREADS + GC_printf1("Starting thread 0x%lx\n", pthread_self()); + GC_printf1("pid = %ld\n", (long) getpid()); + GC_printf1("sp = 0x%lx\n", (long) &arg); + GC_printf1("start_routine = 0x%lx\n", start); +# endif + result = (*start)(start_arg); +#if DEBUG_THREADS + GC_printf1("Finishing thread 0x%x\n", pthread_self()); +#endif + me -> status = result; + me -> flags |= FINISHED; + pthread_cleanup_pop(1); + /* Cleanup acquires lock, ensuring that we can't exit */ + /* while a collection that thinks we're alive is trying to stop */ + /* us. */ + return(result); +} + +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg) +{ + int result; + GC_thread t; + pthread_t my_new_thread; + void * stack; + size_t stacksize; + pthread_attr_t new_attr; + int detachstate; + word my_flags = 0; + struct start_info * si = GC_malloc(sizeof(struct start_info)); + /* This is otherwise saved only in an area mmapped by the thread */ + /* library, which isn't visible to the collector. */ + + if (0 == si) return(ENOMEM); + sem_init(&(si -> registered), 0, 0); + si -> start_routine = start_routine; + si -> arg = arg; + LOCK(); + if (!GC_thr_initialized) GC_thr_init(); + if (NULL == attr) { + stack = 0; + (void) pthread_attr_init(&new_attr); + } else { + new_attr = *attr; + } + pthread_attr_getdetachstate(&new_attr, &detachstate); + if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED; + si -> flags = my_flags; + UNLOCK(); + result = pthread_create(new_thread, &new_attr, GC_start_routine, si); + /* Wait until child has been added to the thread table. */ + /* This also ensures that we hold onto si until the child is done */ + /* with it. Thus it doesn't matter whether it is otherwise */ + /* visible to the collector. */ + if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed"); + sem_destroy(&(si -> registered)); + /* pthread_attr_destroy(&new_attr); */ + /* pthread_attr_destroy(&new_attr); */ + return(result); +} + +GC_bool GC_collecting = 0; + /* A hint that we're in the collector and */ + /* holding the allocation lock for an */ + /* extended period. */ + +/* Reasonably fast spin locks. Basically the same implementation */ +/* as STL alloc.h. This isn't really the right way to do this. */ +/* but until the POSIX scheduling mess gets straightened out ... */ + +volatile unsigned int GC_allocate_lock = 0; + + +void GC_lock() +{ +# define low_spin_max 30 /* spin cycles if we suspect uniprocessor */ +# define high_spin_max 1000 /* spin cycles for multiprocessor */ + static unsigned spin_max = low_spin_max; + unsigned my_spin_max; + static unsigned last_spins = 0; + unsigned my_last_spins; + volatile unsigned junk; +# define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk + int i; + + if (!GC_test_and_set(&GC_allocate_lock)) { + return; + } + junk = 0; + my_spin_max = spin_max; + my_last_spins = last_spins; + for (i = 0; i < my_spin_max; i++) { + if (GC_collecting) goto yield; + if (i < my_last_spins/2 || GC_allocate_lock) { + PAUSE; + continue; + } + if (!GC_test_and_set(&GC_allocate_lock)) { + /* + * got it! + * Spinning worked. Thus we're probably not being scheduled + * against the other process with which we were contending. + * Thus it makes sense to spin longer the next time. + */ + last_spins = i; + spin_max = high_spin_max; + return; + } + } + /* We are probably being scheduled against the other process. Sleep. */ + spin_max = low_spin_max; +yield: + for (i = 0;; ++i) { + if (!GC_test_and_set(&GC_allocate_lock)) { + return; + } +# define SLEEP_THRESHOLD 12 + /* nanosleep(<= 2ms) just spins under Linux. We */ + /* want to be careful to avoid that behavior. */ + if (i < SLEEP_THRESHOLD) { + sched_yield(); + } else { + struct timespec ts; + + if (i > 26) i = 26; + /* Don't wait for more than about 60msecs, even */ + /* under extreme contention. */ + ts.tv_sec = 0; + ts.tv_nsec = 1 << i; + nanosleep(&ts, 0); + } + } +} + +# endif /* LINUX_THREADS */ + diff --git a/gc/mach_dep.c b/gc/mach_dep.c new file mode 100644 index 0000000..756adb3 --- /dev/null +++ b/gc/mach_dep.c @@ -0,0 +1,461 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, November 17, 1995 12:13 pm PST */ +# include "gc_priv.h" +# include <stdio.h> +# include <setjmp.h> +# if defined(OS2) || defined(CX_UX) +# define _setjmp(b) setjmp(b) +# define _longjmp(b,v) longjmp(b,v) +# endif +# ifdef AMIGA +# ifndef __GNUC__ +# include <dos/dos.h> +# else +# include <machine/reg.h> +# endif +# endif + +#if defined(__MWERKS__) && !defined(POWERPC) + +asm static void PushMacRegisters() +{ + sub.w #4,sp // reserve space for one parameter. + move.l a2,(sp) + jsr GC_push_one + move.l a3,(sp) + jsr GC_push_one + move.l a4,(sp) + jsr GC_push_one +# if !__option(a6frames) + // <pcb> perhaps a6 should be pushed if stack frames are not being used. + move.l a6,(sp) + jsr GC_push_one +# endif + // skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) + move.l d2,(sp) + jsr GC_push_one + move.l d3,(sp) + jsr GC_push_one + move.l d4,(sp) + jsr GC_push_one + move.l d5,(sp) + jsr GC_push_one + move.l d6,(sp) + jsr GC_push_one + move.l d7,(sp) + jsr GC_push_one + add.w #4,sp // fix stack. + rts +} + +#endif /* __MWERKS__ */ + +/* Routine to mark from registers that are preserved by the C compiler. */ +/* This must be ported to every new architecture. There is a generic */ +/* version at the end, that is likely, but not guaranteed to work */ +/* on your architecture. Run the test_setjmp program to see whether */ +/* there is any chance it will work. */ + +#ifndef USE_GENERIC_PUSH_REGS +void GC_push_regs() +{ +# ifdef RT + register long TMP_SP; /* must be bound to r11 */ +# endif +# if defined(MIPS) && defined(LINUX) +# define call_push(x) asm("move $4," x ";"); asm("jal GC_push_one") + call_push("$2"); + call_push("$3"); + call_push("$16"); + call_push("$17"); + call_push("$18"); + call_push("$19"); + call_push("$20"); + call_push("$21"); + call_push("$22"); + call_push("$23"); + call_push("$30"); +# undef call_push +# endif // MIPS && LINUX +# ifdef VAX + /* VAX - generic code below does not work under 4.2 */ + /* r1 through r5 are caller save, and therefore */ + /* on the stack or dead. */ + asm("pushl r11"); asm("calls $1,_GC_push_one"); + asm("pushl r10"); asm("calls $1,_GC_push_one"); + asm("pushl r9"); asm("calls $1,_GC_push_one"); + asm("pushl r8"); asm("calls $1,_GC_push_one"); + asm("pushl r7"); asm("calls $1,_GC_push_one"); + asm("pushl r6"); asm("calls $1,_GC_push_one"); +# endif +# if defined(M68K) && (defined(SUNOS4) || defined(NEXT)) + /* M68K SUNOS - could be replaced by generic code */ + /* a0, a1 and d1 are caller save */ + /* and therefore are on stack or dead. */ + + asm("subqw #0x4,sp"); /* allocate word on top of stack */ + + asm("movl a2,sp@"); asm("jbsr _GC_push_one"); + asm("movl a3,sp@"); asm("jbsr _GC_push_one"); + asm("movl a4,sp@"); asm("jbsr _GC_push_one"); + asm("movl a5,sp@"); asm("jbsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("movl d1,sp@"); asm("jbsr _GC_push_one"); + asm("movl d2,sp@"); asm("jbsr _GC_push_one"); + asm("movl d3,sp@"); asm("jbsr _GC_push_one"); + asm("movl d4,sp@"); asm("jbsr _GC_push_one"); + asm("movl d5,sp@"); asm("jbsr _GC_push_one"); + asm("movl d6,sp@"); asm("jbsr _GC_push_one"); + asm("movl d7,sp@"); asm("jbsr _GC_push_one"); + + asm("addqw #0x4,sp"); /* put stack back where it was */ +# endif + +# if defined(M68K) && defined(HP) + /* M68K HP - could be replaced by generic code */ + /* a0, a1 and d1 are caller save. */ + + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d1,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# endif /* M68K HP */ + +# if defined(M68K) && defined(AMIGA) + /* AMIGA - could be replaced by generic code */ + /* a0, a1, d0 and d1 are caller save */ + +# ifdef __GNUC__ + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %a6,(%sp)"); asm("jsr _GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d2,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr _GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr _GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# else /* !__GNUC__ */ + GC_push_one(getreg(REG_A2)); + GC_push_one(getreg(REG_A3)); + GC_push_one(getreg(REG_A4)); + GC_push_one(getreg(REG_A5)); + GC_push_one(getreg(REG_A6)); + /* Skip stack pointer */ + GC_push_one(getreg(REG_D2)); + GC_push_one(getreg(REG_D3)); + GC_push_one(getreg(REG_D4)); + GC_push_one(getreg(REG_D5)); + GC_push_one(getreg(REG_D6)); + GC_push_one(getreg(REG_D7)); +# endif /* !__GNUC__ */ +# endif /* AMIGA */ + +# if defined(M68K) && defined(MACOS) +# if defined(THINK_C) +# define PushMacReg(reg) \ + move.l reg,(sp) \ + jsr GC_push_one + asm { + sub.w #4,sp ; reserve space for one parameter. + PushMacReg(a2); + PushMacReg(a3); + PushMacReg(a4); + ; skip a5 (globals), a6 (frame pointer), and a7 (stack pointer) + PushMacReg(d2); + PushMacReg(d3); + PushMacReg(d4); + PushMacReg(d5); + PushMacReg(d6); + PushMacReg(d7); + add.w #4,sp ; fix stack. + } +# undef PushMacReg +# endif /* THINK_C */ +# if defined(__MWERKS__) + PushMacRegisters(); +# endif /* __MWERKS__ */ +# endif /* MACOS */ + +# if defined(I386) &&!defined(OS2) &&!defined(SVR4) &&!defined(MSWIN32) \ + && !defined(SCO) && !defined(SCO_ELF) \ + && !(defined(LINUX) && defined(__ELF__)) \ + && !(defined(__FreeBSD__) && defined(__ELF__)) \ + && !defined(DOS4GW) + /* I386 code, generic code does not appear to work */ + /* It does appear to work under OS2, and asms dont */ + /* This is used for some 38g UNIX variants and for CYGWIN32 */ + asm("pushl %eax"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ecx"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edx"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebp"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %esi"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edi"); asm("call _GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebx"); asm("call _GC_push_one"); asm("addl $4,%esp"); +# endif + +# if ( defined(I386) && defined(LINUX) && defined(__ELF__) ) \ + || ( defined(I386) && defined(__FreeBSD__) && defined(__ELF__) ) + + /* This is modified for Linux with ELF (Note: _ELF_ only) */ + /* This section handles FreeBSD with ELF. */ + asm("pushl %eax"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ecx"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edx"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebx"); asm("call GC_push_one"); asm("addl $4,%esp"); +# endif + +# if defined(I386) && defined(MSWIN32) && !defined(USE_GENERIC) + /* I386 code, Microsoft variant */ + __asm push eax + __asm call GC_push_one + __asm add esp,4 + __asm push ebx + __asm call GC_push_one + __asm add esp,4 + __asm push ecx + __asm call GC_push_one + __asm add esp,4 + __asm push edx + __asm call GC_push_one + __asm add esp,4 + __asm push ebp + __asm call GC_push_one + __asm add esp,4 + __asm push esi + __asm call GC_push_one + __asm add esp,4 + __asm push edi + __asm call GC_push_one + __asm add esp,4 +# endif + +# if defined(I386) && (defined(SVR4) || defined(SCO) || defined(SCO_ELF)) + /* I386 code, SVR4 variant, generic code does not appear to work */ + asm("pushl %eax"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebx"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ecx"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edx"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %ebp"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %esi"); asm("call GC_push_one"); asm("addl $4,%esp"); + asm("pushl %edi"); asm("call GC_push_one"); asm("addl $4,%esp"); +# endif + +# ifdef NS32K + asm ("movd r3, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); + asm ("movd r4, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); + asm ("movd r5, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); + asm ("movd r6, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); + asm ("movd r7, tos"); asm ("bsr ?_GC_push_one"); asm ("adjspb $-4"); +# endif + +# ifdef SPARC + { + word GC_save_regs_in_stack(); + + /* generic code will not work */ + (void)GC_save_regs_in_stack(); + } +# endif + +# ifdef RT + GC_push_one(TMP_SP); /* GC_push_one from r11 */ + + asm("cas r11, r6, r0"); GC_push_one(TMP_SP); /* r6 */ + asm("cas r11, r7, r0"); GC_push_one(TMP_SP); /* through */ + asm("cas r11, r8, r0"); GC_push_one(TMP_SP); /* r10 */ + asm("cas r11, r9, r0"); GC_push_one(TMP_SP); + asm("cas r11, r10, r0"); GC_push_one(TMP_SP); + + asm("cas r11, r12, r0"); GC_push_one(TMP_SP); /* r12 */ + asm("cas r11, r13, r0"); GC_push_one(TMP_SP); /* through */ + asm("cas r11, r14, r0"); GC_push_one(TMP_SP); /* r15 */ + asm("cas r11, r15, r0"); GC_push_one(TMP_SP); +# endif + +# if defined(M68K) && defined(SYSV) + /* Once again similar to SUN and HP, though setjmp appears to work. + --Parag + */ +# ifdef __GNUC__ + asm("subqw #0x4,%sp"); /* allocate word on top of stack */ + + asm("movl %a2,%sp@"); asm("jbsr GC_push_one"); + asm("movl %a3,%sp@"); asm("jbsr GC_push_one"); + asm("movl %a4,%sp@"); asm("jbsr GC_push_one"); + asm("movl %a5,%sp@"); asm("jbsr GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("movl %d1,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d2,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d3,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d4,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d5,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d6,%sp@"); asm("jbsr GC_push_one"); + asm("movl %d7,%sp@"); asm("jbsr GC_push_one"); + + asm("addqw #0x4,%sp"); /* put stack back where it was */ +# else /* !__GNUC__*/ + asm("subq.w &0x4,%sp"); /* allocate word on top of stack */ + + asm("mov.l %a2,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %a3,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %a4,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %a5,(%sp)"); asm("jsr GC_push_one"); + /* Skip frame pointer and stack pointer */ + asm("mov.l %d1,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d2,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d3,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d4,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d5,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d6,(%sp)"); asm("jsr GC_push_one"); + asm("mov.l %d7,(%sp)"); asm("jsr GC_push_one"); + + asm("addq.w &0x4,%sp"); /* put stack back where it was */ +# endif /* !__GNUC__ */ +# endif /* M68K/SYSV */ + + + /* other machines... */ +# if !(defined M68K) && !(defined VAX) && !(defined RT) +# if !(defined SPARC) && !(defined I386) && !(defined NS32K) +# if !defined(POWERPC) && !defined(UTS4) +# if (!defined(MIPS) && !defined(LINUX)) + --> bad news <-- +# endif +# endif +# endif +# endif +} +#endif /* !USE_GENERIC_PUSH_REGS */ + +#if defined(USE_GENERIC_PUSH_REGS) +void GC_generic_push_regs(cold_gc_frame) +ptr_t cold_gc_frame; +{ + /* Generic code */ + /* The idea is due to Parag Patel at HP. */ + /* We're not sure whether he would like */ + /* to be he acknowledged for it or not. */ + { + static jmp_buf regs; + register word * i = (word *) regs; + register ptr_t lim = (ptr_t)(regs) + (sizeof regs); + + /* Setjmp on Sun 3s doesn't clear all of the buffer. */ + /* That tends to preserve garbage. Clear it. */ + for (; (char *)i < lim; i++) { + *i = 0; + } +# if defined(POWERPC) || defined(MSWIN32) || defined(UTS4) + (void) setjmp(regs); +# else + (void) _setjmp(regs); +# endif + GC_push_current_stack(cold_gc_frame); + } +} +#endif /* USE_GENERIC_PUSH_REGS */ + +/* On register window machines, we need a way to force registers into */ +/* the stack. Return sp. */ +# ifdef SPARC + asm(" .seg \"text\""); +# ifdef SVR4 + asm(" .globl GC_save_regs_in_stack"); + asm("GC_save_regs_in_stack:"); + asm(" .type GC_save_regs_in_stack,#function"); +# else + asm(" .globl _GC_save_regs_in_stack"); + asm("_GC_save_regs_in_stack:"); +# endif + asm(" ta 0x3 ! ST_FLUSH_WINDOWS"); + asm(" mov %sp,%o0"); + asm(" retl"); + asm(" nop"); +# ifdef SVR4 + asm(" .GC_save_regs_in_stack_end:"); + asm(" .size GC_save_regs_in_stack,.GC_save_regs_in_stack_end-GC_save_regs_in_stack"); +# endif +# ifdef LINT + word GC_save_regs_in_stack() { return(0 /* sp really */);} +# endif +# endif + + +/* GC_clear_stack_inner(arg, limit) clears stack area up to limit and */ +/* returns arg. Stack clearing is crucial on SPARC, so we supply */ +/* an assembly version that's more careful. Assumes limit is hotter */ +/* than sp, and limit is 8 byte aligned. */ +#if defined(ASM_CLEAR_CODE) && !defined(THREADS) +#ifndef SPARC + --> fix it +#endif +# ifdef SUNOS4 + asm(".globl _GC_clear_stack_inner"); + asm("_GC_clear_stack_inner:"); +# else + asm(".globl GC_clear_stack_inner"); + asm("GC_clear_stack_inner:"); + asm(".type GC_save_regs_in_stack,#function"); +# endif + asm("mov %sp,%o2"); /* Save sp */ + asm("add %sp,-8,%o3"); /* p = sp-8 */ + asm("clr %g1"); /* [g0,g1] = 0 */ + asm("add %o1,-0x60,%sp"); /* Move sp out of the way, */ + /* so that traps still work. */ + /* Includes some extra words */ + /* so we can be sloppy below. */ + asm("loop:"); + asm("std %g0,[%o3]"); /* *(long long *)p = 0 */ + asm("cmp %o3,%o1"); + asm("bgu loop "); /* if (p > limit) goto loop */ + asm("add %o3,-8,%o3"); /* p -= 8 (delay slot) */ + asm("retl"); + asm("mov %o2,%sp"); /* Restore sp., delay slot */ + /* First argument = %o0 = return value */ +# ifdef SVR4 + asm(" .GC_clear_stack_inner_end:"); + asm(" .size GC_clear_stack_inner,.GC_clear_stack_inner_end-GC_clear_stack_inner"); +# endif + +# ifdef LINT + /*ARGSUSED*/ + ptr_t GC_clear_stack_inner(arg, limit) + ptr_t arg; word limit; + { return(arg); } +# endif +#endif diff --git a/gc/makefile.depend b/gc/makefile.depend new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gc/makefile.depend diff --git a/gc/malloc.c b/gc/malloc.c new file mode 100644 index 0000000..66e62d2 --- /dev/null +++ b/gc/malloc.c @@ -0,0 +1,443 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 7, 1996 4:32 pm PST */ + +#include <stdio.h> +#include "gc_priv.h" + +extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */ +void GC_extend_size_map(); /* in misc.c. */ + +/* Allocate reclaim list for kind: */ +/* Return TRUE on success */ +GC_bool GC_alloc_reclaim_list(kind) +register struct obj_kind * kind; +{ + struct hblk ** result = (struct hblk **) + GC_scratch_alloc((MAXOBJSZ+1) * sizeof(struct hblk *)); + if (result == 0) return(FALSE); + BZERO(result, (MAXOBJSZ+1)*sizeof(struct hblk *)); + kind -> ok_reclaim_list = result; + return(TRUE); +} + +/* allocate lb bytes for an object of kind. */ +/* Should not be used to directly to allocate */ +/* objects such as STUBBORN objects that */ +/* require special handling on allocation. */ +/* First a version that assumes we already */ +/* hold lock: */ +ptr_t GC_generic_malloc_inner(lb, k) +register word lb; +register int k; +{ +register word lw; +register ptr_t op; +register ptr_t *opp; + + if( SMALL_OBJ(lb) ) { + register struct obj_kind * kind = GC_obj_kinds + k; +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); + if (lw == 0) lw = 1; +# endif + opp = &(kind -> ok_freelist[lw]); + if( (op = *opp) == 0 ) { +# ifdef MERGE_SIZES + if (GC_size_map[lb] == 0) { + if (!GC_is_initialized) GC_init_inner(); + if (GC_size_map[lb] == 0) GC_extend_size_map(lb); + return(GC_generic_malloc_inner(lb, k)); + } +# else + if (!GC_is_initialized) { + GC_init_inner(); + return(GC_generic_malloc_inner(lb, k)); + } +# endif + if (kind -> ok_reclaim_list == 0) { + if (!GC_alloc_reclaim_list(kind)) goto out; + } + op = GC_allocobj(lw, k); + if (op == 0) goto out; + } + /* Here everything is in a consistent state. */ + /* We assume the following assignment is */ + /* atomic. If we get aborted */ + /* after the assignment, we lose an object, */ + /* but that's benign. */ + /* Volatile declarations may need to be added */ + /* to prevent the compiler from breaking things.*/ + *opp = obj_link(op); + obj_link(op) = 0; + } else { + register struct hblk * h; + register word n_blocks = divHBLKSZ(ADD_SLOP(lb) + + HDR_BYTES + HBLKSIZE-1); + + if (!GC_is_initialized) GC_init_inner(); + /* Do our share of marking work */ + if(GC_incremental && !GC_dont_gc) + GC_collect_a_little_inner((int)n_blocks); + lw = ROUNDED_UP_WORDS(lb); + h = GC_allochblk(lw, k, 0); +# ifdef USE_MUNMAP + if (0 == h) { + GC_merge_unmapped(); + h = GC_allochblk(lw, k, 0); + } +# endif + while (0 == h && GC_collect_or_expand(n_blocks, FALSE)) { + h = GC_allochblk(lw, k, 0); + } + if (h == 0) { + op = 0; + } else { + op = (ptr_t) (h -> hb_body); + GC_words_wasted += BYTES_TO_WORDS(n_blocks * HBLKSIZE) - lw; + } + } + GC_words_allocd += lw; + +out: + return((ptr_t)op); +} + +ptr_t GC_generic_malloc(lb, k) +register word lb; +register int k; +{ + ptr_t result; + DCL_LOCK_STATE; + + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + result = GC_generic_malloc_inner(lb, k); + UNLOCK(); + ENABLE_SIGNALS(); + if (0 == result) { + return((*GC_oom_fn)(lb)); + } else { + return(result); + } +} + + +#define GENERAL_MALLOC(lb,k) \ + (GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k)) +/* We make the GC_clear_stack_call a tail call, hoping to get more of */ +/* the stack. */ + +/* Allocate lb bytes of atomic (pointerfree) data */ +# ifdef __STDC__ + GC_PTR GC_malloc_atomic(size_t lb) +# else + GC_PTR GC_malloc_atomic(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t * opp; +register word lw; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_aobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + return(GENERAL_MALLOC((word)lb, PTRFREE)); + } + /* See above comment on signals. */ + *opp = obj_link(op); + GC_words_allocd += lw; + FASTUNLOCK(); + return((GC_PTR) op); + } else { + return(GENERAL_MALLOC((word)lb, PTRFREE)); + } +} + +/* Allocate lb bytes of composite (pointerful) data */ +# ifdef __STDC__ + GC_PTR GC_malloc(size_t lb) +# else + GC_PTR GC_malloc(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t *opp; +register word lw; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_objfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + return(GENERAL_MALLOC((word)lb, NORMAL)); + } + /* See above comment on signals. */ + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + FASTUNLOCK(); + return((GC_PTR) op); + } else { + return(GENERAL_MALLOC((word)lb, NORMAL)); + } +} + +# ifdef REDIRECT_MALLOC +# ifdef __STDC__ + GC_PTR malloc(size_t lb) +# else + GC_PTR malloc(lb) + size_t lb; +# endif + { + /* It might help to manually inline the GC_malloc call here. */ + /* But any decent compiler should reduce the extra procedure call */ + /* to at most a jump instruction in this case. */ +# if defined(I386) && defined(SOLARIS_THREADS) + /* + * Thread initialisation can call malloc before + * we're ready for it. + * It's not clear that this is enough to help matters. + * The thread implementation may well call malloc at other + * inopportune times. + */ + if (!GC_is_initialized) return sbrk(lb); +# endif /* I386 && SOLARIS_THREADS */ + return(REDIRECT_MALLOC(lb)); + } + +# ifdef __STDC__ + GC_PTR calloc(size_t n, size_t lb) +# else + GC_PTR calloc(n, lb) + size_t n, lb; +# endif + { + return(REDIRECT_MALLOC(n*lb)); + } +# endif /* REDIRECT_MALLOC */ + +GC_PTR GC_generic_or_special_malloc(lb,knd) +word lb; +int knd; +{ + switch(knd) { +# ifdef STUBBORN_ALLOC + case STUBBORN: + return(GC_malloc_stubborn((size_t)lb)); +# endif + case PTRFREE: + return(GC_malloc_atomic((size_t)lb)); + case NORMAL: + return(GC_malloc((size_t)lb)); + case UNCOLLECTABLE: + return(GC_malloc_uncollectable((size_t)lb)); +# ifdef ATOMIC_UNCOLLECTABLE + case AUNCOLLECTABLE: + return(GC_malloc_atomic_uncollectable((size_t)lb)); +# endif /* ATOMIC_UNCOLLECTABLE */ + default: + return(GC_generic_malloc(lb,knd)); + } +} + + +/* Change the size of the block pointed to by p to contain at least */ +/* lb bytes. The object may be (and quite likely will be) moved. */ +/* The kind (e.g. atomic) is the same as that of the old. */ +/* Shrinking of large blocks is not implemented well. */ +# ifdef __STDC__ + GC_PTR GC_realloc(GC_PTR p, size_t lb) +# else + GC_PTR GC_realloc(p,lb) + GC_PTR p; + size_t lb; +# endif +{ +register struct hblk * h; +register hdr * hhdr; +register word sz; /* Current size in bytes */ +register word orig_sz; /* Original sz in bytes */ +int obj_kind; + + if (p == 0) return(GC_malloc(lb)); /* Required by ANSI */ + h = HBLKPTR(p); + hhdr = HDR(h); + sz = hhdr -> hb_sz; + obj_kind = hhdr -> hb_obj_kind; + sz = WORDS_TO_BYTES(sz); + orig_sz = sz; + + if (sz > WORDS_TO_BYTES(MAXOBJSZ)) { + /* Round it up to the next whole heap block */ + register word descr; + + sz = (sz+HDR_BYTES+HBLKSIZE-1) + & (~HBLKMASK); + sz -= HDR_BYTES; + hhdr -> hb_sz = BYTES_TO_WORDS(sz); + descr = GC_obj_kinds[obj_kind].ok_descriptor; + if (GC_obj_kinds[obj_kind].ok_relocate_descr) descr += sz; + hhdr -> hb_descr = descr; + if (IS_UNCOLLECTABLE(obj_kind)) GC_non_gc_bytes += (sz - orig_sz); + /* Extra area is already cleared by allochblk. */ + } + if (ADD_SLOP(lb) <= sz) { + if (lb >= (sz >> 1)) { +# ifdef STUBBORN_ALLOC + if (obj_kind == STUBBORN) GC_change_stubborn(p); +# endif + if (orig_sz > lb) { + /* Clear unneeded part of object to avoid bogus pointer */ + /* tracing. */ + /* Safe for stubborn objects. */ + BZERO(((ptr_t)p) + lb, orig_sz - lb); + } + return(p); + } else { + /* shrink */ + GC_PTR result = + GC_generic_or_special_malloc((word)lb, obj_kind); + + if (result == 0) return(0); + /* Could also return original object. But this */ + /* gives the client warning of imminent disaster. */ + BCOPY(p, result, lb); +# ifndef IGNORE_FREE + GC_free(p); +# endif + return(result); + } + } else { + /* grow */ + GC_PTR result = + GC_generic_or_special_malloc((word)lb, obj_kind); + + if (result == 0) return(0); + BCOPY(p, result, sz); +# ifndef IGNORE_FREE + GC_free(p); +# endif + return(result); + } +} + +# ifdef REDIRECT_MALLOC +# ifdef __STDC__ + GC_PTR realloc(GC_PTR p, size_t lb) +# else + GC_PTR realloc(p,lb) + GC_PTR p; + size_t lb; +# endif + { + return(GC_realloc(p, lb)); + } +# endif /* REDIRECT_MALLOC */ + +/* Explicitly deallocate an object p. */ +# ifdef __STDC__ + void GC_free(GC_PTR p) +# else + void GC_free(p) + GC_PTR p; +# endif +{ + register struct hblk *h; + register hdr *hhdr; + register signed_word sz; + register ptr_t * flh; + register int knd; + register struct obj_kind * ok; + DCL_LOCK_STATE; + + if (p == 0) return; + /* Required by ANSI. It's not my fault ... */ + h = HBLKPTR(p); + hhdr = HDR(h); +# if defined(REDIRECT_MALLOC) && \ + (defined(SOLARIS_THREADS) || defined(LINUX_THREADS)) + /* We have to redirect malloc calls during initialization. */ + /* Don't try to deallocate that memory. */ + if (0 == hhdr) return; +# endif + knd = hhdr -> hb_obj_kind; + sz = hhdr -> hb_sz; + ok = &GC_obj_kinds[knd]; + if (sz <= MAXOBJSZ) { +# ifdef THREADS + DISABLE_SIGNALS(); + LOCK(); +# endif + GC_mem_freed += sz; + /* A signal here can make GC_mem_freed and GC_non_gc_bytes */ + /* inconsistent. We claim this is benign. */ + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= WORDS_TO_BYTES(sz); + /* Its unnecessary to clear the mark bit. If the */ + /* object is reallocated, it doesn't matter. O.w. the */ + /* collector will do it, since it's on a free list. */ + if (ok -> ok_init) { + BZERO((word *)p + 1, WORDS_TO_BYTES(sz-1)); + } + flh = &(ok -> ok_freelist[sz]); + obj_link(p) = *flh; + *flh = (ptr_t)p; +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + } else { + DISABLE_SIGNALS(); + LOCK(); + GC_mem_freed += sz; + if (IS_UNCOLLECTABLE(knd)) GC_non_gc_bytes -= WORDS_TO_BYTES(sz); + GC_freehblk(h); + UNLOCK(); + ENABLE_SIGNALS(); + } +} + +# ifdef REDIRECT_MALLOC +# ifdef __STDC__ + void free(GC_PTR p) +# else + void free(p) + GC_PTR p; +# endif + { +# ifndef IGNORE_FREE + GC_free(p); +# endif + } +# endif /* REDIRECT_MALLOC */ diff --git a/gc/mallocx.c b/gc/mallocx.c new file mode 100644 index 0000000..8c07fa9 --- /dev/null +++ b/gc/mallocx.c @@ -0,0 +1,375 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * These are extra allocation routines which are likely to be less + * frequently used than those in malloc.c. They are separate in the + * hope that the .o file will be excluded from statically linked + * executables. We should probably break this up further. + */ + +#include <stdio.h> +#include "gc_priv.h" + +extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */ +void GC_extend_size_map(); /* in misc.c. */ +GC_bool GC_alloc_reclaim_list(); /* in malloc.c */ + +/* Some externally visible but unadvertised variables to allow access to */ +/* free lists from inlined allocators without including gc_priv.h */ +/* or introducing dependencies on internal data structure layouts. */ +ptr_t * CONST GC_objfreelist_ptr = GC_objfreelist; +ptr_t * CONST GC_aobjfreelist_ptr = GC_aobjfreelist; +ptr_t * CONST GC_uobjfreelist_ptr = GC_uobjfreelist; +# ifdef ATOMIC_UNCOLLECTABLE + ptr_t * CONST GC_auobjfreelist_ptr = GC_auobjfreelist; +# endif + +/* Allocate a composite object of size n bytes. The caller guarantees */ +/* that pointers past the first page are not relevant. Caller holds */ +/* allocation lock. */ +ptr_t GC_generic_malloc_inner_ignore_off_page(lb, k) +register size_t lb; +register int k; +{ + register struct hblk * h; + register word n_blocks; + register word lw; + register ptr_t op; + + if (lb <= HBLKSIZE) + return(GC_generic_malloc_inner((word)lb, k)); + n_blocks = divHBLKSZ(ADD_SLOP(lb) + HDR_BYTES + HBLKSIZE-1); + if (!GC_is_initialized) GC_init_inner(); + /* Do our share of marking work */ + if(GC_incremental && !GC_dont_gc) + GC_collect_a_little_inner((int)n_blocks); + lw = ROUNDED_UP_WORDS(lb); + h = GC_allochblk(lw, k, IGNORE_OFF_PAGE); +# ifdef USE_MUNMAP + if (0 == h) { + GC_merge_unmapped(); + h = GC_allochblk(lw, k, IGNORE_OFF_PAGE); + } +# endif + while (0 == h && GC_collect_or_expand(n_blocks, TRUE)) { + h = GC_allochblk(lw, k, IGNORE_OFF_PAGE); + } + if (h == 0) { + op = 0; + } else { + op = (ptr_t) (h -> hb_body); + GC_words_wasted += BYTES_TO_WORDS(n_blocks * HBLKSIZE) - lw; + } + GC_words_allocd += lw; + return((ptr_t)op); +} + +ptr_t GC_generic_malloc_ignore_off_page(lb, k) +register size_t lb; +register int k; +{ + register ptr_t result; + DCL_LOCK_STATE; + + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + result = GC_generic_malloc_inner_ignore_off_page(lb,k); + UNLOCK(); + ENABLE_SIGNALS(); + if (0 == result) { + return((*GC_oom_fn)(lb)); + } else { + return(result); + } +} + +# if defined(__STDC__) || defined(__cplusplus) + void * GC_malloc_ignore_off_page(size_t lb) +# else + char * GC_malloc_ignore_off_page(lb) + register size_t lb; +# endif +{ + return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, NORMAL)); +} + +# if defined(__STDC__) || defined(__cplusplus) + void * GC_malloc_atomic_ignore_off_page(size_t lb) +# else + char * GC_malloc_atomic_ignore_off_page(lb) + register size_t lb; +# endif +{ + return((GC_PTR)GC_generic_malloc_ignore_off_page(lb, PTRFREE)); +} + +/* Increment GC_words_allocd from code that doesn't have direct access */ +/* to GC_arrays. */ +# ifdef __STDC__ +void GC_incr_words_allocd(size_t n) +{ + GC_words_allocd += n; +} + +/* The same for GC_mem_freed. */ +void GC_incr_mem_freed(size_t n) +{ + GC_mem_freed += n; +} +# endif /* __STDC__ */ + +/* Analogous to the above, but assumes a small object size, and */ +/* bypasses MERGE_SIZES mechanism. Used by gc_inline.h. */ +#ifdef __STDC__ + ptr_t GC_generic_malloc_words_small(size_t lw, int k) +#else + ptr_t GC_generic_malloc_words_small(lw, k) + register word lw; + register int k; +#endif +{ +register ptr_t op; +register ptr_t *opp; +register struct obj_kind * kind = GC_obj_kinds + k; +DCL_LOCK_STATE; + + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + opp = &(kind -> ok_freelist[lw]); + if( (op = *opp) == 0 ) { + if (!GC_is_initialized) { + GC_init_inner(); + } + if (kind -> ok_reclaim_list != 0 || GC_alloc_reclaim_list(kind)) { + op = GC_clear_stack(GC_allocobj((word)lw, k)); + } + if (op == 0) { + UNLOCK(); + ENABLE_SIGNALS(); + return ((*GC_oom_fn)(WORDS_TO_BYTES(lw))); + } + } + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + UNLOCK(); + ENABLE_SIGNALS(); + return((ptr_t)op); +} + +#if defined(THREADS) && !defined(SRC_M3) +/* Return a list of 1 or more objects of the indicated size, linked */ +/* through the first word in the object. This has the advantage that */ +/* it acquires the allocation lock only once, and may greatly reduce */ +/* time wasted contending for the allocation lock. Typical usage would */ +/* be in a thread that requires many items of the same size. It would */ +/* keep its own free list in thread-local storage, and call */ +/* GC_malloc_many or friends to replenish it. (We do not round up */ +/* object sizes, since a call indicates the intention to consume many */ +/* objects of exactly this size.) */ +/* Note that the client should usually clear the link field. */ +ptr_t GC_generic_malloc_many(lb, k) +register word lb; +register int k; +{ +ptr_t op; +register ptr_t p; +ptr_t *opp; +word lw; +register word my_words_allocd; +DCL_LOCK_STATE; + + if (!SMALL_OBJ(lb)) { + op = GC_generic_malloc(lb, k); + if(0 != op) obj_link(op) = 0; + return(op); + } + lw = ALIGNED_WORDS(lb); + GC_INVOKE_FINALIZERS(); + DISABLE_SIGNALS(); + LOCK(); + opp = &(GC_obj_kinds[k].ok_freelist[lw]); + if( (op = *opp) == 0 ) { + if (!GC_is_initialized) { + GC_init_inner(); + } + op = GC_clear_stack(GC_allocobj(lw, k)); + if (op == 0) { + UNLOCK(); + ENABLE_SIGNALS(); + op = (*GC_oom_fn)(lb); + if(0 != op) obj_link(op) = 0; + return(op); + } + } + *opp = 0; + my_words_allocd = 0; + for (p = op; p != 0; p = obj_link(p)) { + my_words_allocd += lw; + if (my_words_allocd >= BODY_SZ) { + *opp = obj_link(p); + obj_link(p) = 0; + break; + } + } + GC_words_allocd += my_words_allocd; + +out: + UNLOCK(); + ENABLE_SIGNALS(); + return(op); + +} + +void * GC_malloc_many(size_t lb) +{ + return(GC_generic_malloc_many(lb, NORMAL)); +} + +/* Note that the "atomic" version of this would be unsafe, since the */ +/* links would not be seen by the collector. */ +# endif + +/* Allocate lb bytes of pointerful, traced, but not collectable data */ +# ifdef __STDC__ + GC_PTR GC_malloc_uncollectable(size_t lb) +# else + GC_PTR GC_malloc_uncollectable(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t *opp; +register word lw; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES +# ifdef ADD_BYTE_AT_END + if (lb != 0) lb--; + /* We don't need the extra byte, since this won't be */ + /* collected anyway. */ +# endif + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_uobjfreelist[lw]); + FASTLOCK(); + if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) { + /* See above comment on signals. */ + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + /* Mark bit ws already set on free list. It will be */ + /* cleared only temporarily during a collection, as a */ + /* result of the normal free list mark bit clearing. */ + GC_non_gc_bytes += WORDS_TO_BYTES(lw); + FASTUNLOCK(); + return((GC_PTR) op); + } + FASTUNLOCK(); + op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); + } else { + op = (ptr_t)GC_generic_malloc((word)lb, UNCOLLECTABLE); + } + if (0 == op) return(0); + /* We don't need the lock here, since we have an undisguised */ + /* pointer. We do need to hold the lock while we adjust */ + /* mark bits. */ + { + register struct hblk * h; + + h = HBLKPTR(op); + lw = HDR(h) -> hb_sz; + + DISABLE_SIGNALS(); + LOCK(); + GC_set_mark_bit(op); + GC_non_gc_bytes += WORDS_TO_BYTES(lw); + UNLOCK(); + ENABLE_SIGNALS(); + return((GC_PTR) op); + } +} + +# ifdef ATOMIC_UNCOLLECTABLE +/* Allocate lb bytes of pointerfree, untraced, uncollectable data */ +/* This is normally roughly equivalent to the system malloc. */ +/* But it may be useful if malloc is redefined. */ +# ifdef __STDC__ + GC_PTR GC_malloc_atomic_uncollectable(size_t lb) +# else + GC_PTR GC_malloc_atomic_uncollectable(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t *opp; +register word lw; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES +# ifdef ADD_BYTE_AT_END + if (lb != 0) lb--; + /* We don't need the extra byte, since this won't be */ + /* collected anyway. */ +# endif + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_auobjfreelist[lw]); + FASTLOCK(); + if( FASTLOCK_SUCCEEDED() && (op = *opp) != 0 ) { + /* See above comment on signals. */ + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + /* Mark bit was already set while object was on free list. */ + GC_non_gc_bytes += WORDS_TO_BYTES(lw); + FASTUNLOCK(); + return((GC_PTR) op); + } + FASTUNLOCK(); + op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE); + } else { + op = (ptr_t)GC_generic_malloc((word)lb, AUNCOLLECTABLE); + } + if (0 == op) return(0); + /* We don't need the lock here, since we have an undisguised */ + /* pointer. We do need to hold the lock while we adjust */ + /* mark bits. */ + { + register struct hblk * h; + + h = HBLKPTR(op); + lw = HDR(h) -> hb_sz; + + DISABLE_SIGNALS(); + LOCK(); + GC_set_mark_bit(op); + GC_non_gc_bytes += WORDS_TO_BYTES(lw); + UNLOCK(); + ENABLE_SIGNALS(); + return((GC_PTR) op); + } +} + +#endif /* ATOMIC_UNCOLLECTABLE */ diff --git a/gc/mark.c b/gc/mark.c new file mode 100644 index 0000000..34db472 --- /dev/null +++ b/gc/mark.c @@ -0,0 +1,1161 @@ + +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ + + +# include <stdio.h> +# include "gc_priv.h" +# include "gc_mark.h" + +/* We put this here to minimize the risk of inlining. */ +/*VARARGS*/ +#ifdef __WATCOMC__ + void GC_noop(void *p, ...) {} +#else + void GC_noop() {} +#endif + +/* Single argument version, robust against whole program analysis. */ +void GC_noop1(x) +word x; +{ + static VOLATILE word sink; + + sink = x; +} + +/* mark_proc GC_mark_procs[MAX_MARK_PROCS] = {0} -- declared in gc_priv.h */ + +word GC_n_mark_procs = 0; + +/* Initialize GC_obj_kinds properly and standard free lists properly. */ +/* This must be done statically since they may be accessed before */ +/* GC_init is called. */ +/* It's done here, since we need to deal with mark descriptors. */ +struct obj_kind GC_obj_kinds[MAXOBJKINDS] = { +/* PTRFREE */ { &GC_aobjfreelist[0], 0 /* filled in dynamically */, + 0 | DS_LENGTH, FALSE, FALSE }, +/* NORMAL */ { &GC_objfreelist[0], 0, +# if defined(ADD_BYTE_AT_END) && ALIGNMENT > DS_TAGS + (word)(-ALIGNMENT) | DS_LENGTH, +# else + 0 | DS_LENGTH, +# endif + TRUE /* add length to descr */, TRUE }, +/* UNCOLLECTABLE */ + { &GC_uobjfreelist[0], 0, + 0 | DS_LENGTH, TRUE /* add length to descr */, TRUE }, +# ifdef ATOMIC_UNCOLLECTABLE + /* AUNCOLLECTABLE */ + { &GC_auobjfreelist[0], 0, + 0 | DS_LENGTH, FALSE /* add length to descr */, FALSE }, +# endif +# ifdef STUBBORN_ALLOC +/*STUBBORN*/ { &GC_sobjfreelist[0], 0, + 0 | DS_LENGTH, TRUE /* add length to descr */, TRUE }, +# endif +}; + +# ifdef ATOMIC_UNCOLLECTABLE +# ifdef STUBBORN_ALLOC + int GC_n_kinds = 5; +# else + int GC_n_kinds = 4; +# endif +# else +# ifdef STUBBORN_ALLOC + int GC_n_kinds = 4; +# else + int GC_n_kinds = 3; +# endif +# endif + + +# ifndef INITIAL_MARK_STACK_SIZE +# define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) + /* INITIAL_MARK_STACK_SIZE * sizeof(mse) should be a */ + /* multiple of HBLKSIZE. */ +# endif + +/* + * Limits of stack for GC_mark routine. + * All ranges between GC_mark_stack(incl.) and GC_mark_stack_top(incl.) still + * need to be marked from. + */ + +word GC_n_rescuing_pages; /* Number of dirty pages we marked from */ + /* excludes ptrfree pages, etc. */ + +mse * GC_mark_stack; + +word GC_mark_stack_size = 0; + +mse * GC_mark_stack_top; + +static struct hblk * scan_ptr; + +mark_state_t GC_mark_state = MS_NONE; + +GC_bool GC_mark_stack_too_small = FALSE; + +GC_bool GC_objects_are_marked = FALSE; /* Are there collectable marked */ + /* objects in the heap? */ + +/* Is a collection in progress? Note that this can return true in the */ +/* nonincremental case, if a collection has been abandoned and the */ +/* mark state is now MS_INVALID. */ +GC_bool GC_collection_in_progress() +{ + return(GC_mark_state != MS_NONE); +} + +/* clear all mark bits in the header */ +void GC_clear_hdr_marks(hhdr) +register hdr * hhdr; +{ + BZERO(hhdr -> hb_marks, MARK_BITS_SZ*sizeof(word)); +} + +/* Set all mark bits in the header. Used for uncollectable blocks. */ +void GC_set_hdr_marks(hhdr) +register hdr * hhdr; +{ + register int i; + + for (i = 0; i < MARK_BITS_SZ; ++i) { + hhdr -> hb_marks[i] = ONES; + } +} + +/* + * Clear all mark bits associated with block h. + */ +/*ARGSUSED*/ +static void clear_marks_for_block(h, dummy) +struct hblk *h; +word dummy; +{ + register hdr * hhdr = HDR(h); + + if (IS_UNCOLLECTABLE(hhdr -> hb_obj_kind)) return; + /* Mark bit for these is cleared only once the object is */ + /* explicitly deallocated. This either frees the block, or */ + /* the bit is cleared once the object is on the free list. */ + GC_clear_hdr_marks(hhdr); +} + +/* Slow but general routines for setting/clearing/asking about mark bits */ +void GC_set_mark_bit(p) +ptr_t p; +{ + register struct hblk *h = HBLKPTR(p); + register hdr * hhdr = HDR(h); + register int word_no = (word *)p - (word *)h; + + set_mark_bit_from_hdr(hhdr, word_no); +} + +void GC_clear_mark_bit(p) +ptr_t p; +{ + register struct hblk *h = HBLKPTR(p); + register hdr * hhdr = HDR(h); + register int word_no = (word *)p - (word *)h; + + clear_mark_bit_from_hdr(hhdr, word_no); +} + +GC_bool GC_is_marked(p) +ptr_t p; +{ + register struct hblk *h = HBLKPTR(p); + register hdr * hhdr = HDR(h); + register int word_no = (word *)p - (word *)h; + + return(mark_bit_from_hdr(hhdr, word_no)); +} + + +/* + * Clear mark bits in all allocated heap blocks. This invalidates + * the marker invariant, and sets GC_mark_state to reflect this. + * (This implicitly starts marking to reestablish the invariant.) + */ +void GC_clear_marks() +{ + GC_apply_to_all_blocks(clear_marks_for_block, (word)0); + GC_objects_are_marked = FALSE; + GC_mark_state = MS_INVALID; + scan_ptr = 0; +# ifdef GATHERSTATS + /* Counters reflect currently marked objects: reset here */ + GC_composite_in_use = 0; + GC_atomic_in_use = 0; +# endif + +} + +/* Initiate a garbage collection. Initiates a full collection if the */ +/* mark state is invalid. */ +/*ARGSUSED*/ +void GC_initiate_gc() +{ + if (GC_dirty_maintained) GC_read_dirty(); +# ifdef STUBBORN_ALLOC + GC_read_changed(); +# endif +# ifdef CHECKSUMS + { + extern void GC_check_dirty(); + + if (GC_dirty_maintained) GC_check_dirty(); + } +# endif +# ifdef GATHERSTATS + GC_n_rescuing_pages = 0; +# endif + if (GC_mark_state == MS_NONE) { + GC_mark_state = MS_PUSH_RESCUERS; + } else if (GC_mark_state != MS_INVALID) { + ABORT("unexpected state"); + } /* else this is really a full collection, and mark */ + /* bits are invalid. */ + scan_ptr = 0; +} + + +static void alloc_mark_stack(); + +/* Perform a small amount of marking. */ +/* We try to touch roughly a page of memory. */ +/* Return TRUE if we just finished a mark phase. */ +/* Cold_gc_frame is an address inside a GC frame that */ +/* remains valid until all marking is complete. */ +/* A zero value indicates that it's OK to miss some */ +/* register values. */ +GC_bool GC_mark_some(cold_gc_frame) +ptr_t cold_gc_frame; +{ + switch(GC_mark_state) { + case MS_NONE: + return(FALSE); + + case MS_PUSH_RESCUERS: + if (GC_mark_stack_top + >= GC_mark_stack + INITIAL_MARK_STACK_SIZE/4) { + GC_mark_from_mark_stack(); + return(FALSE); + } else { + scan_ptr = GC_push_next_marked_dirty(scan_ptr); + if (scan_ptr == 0) { +# ifdef PRINTSTATS + GC_printf1("Marked from %lu dirty pages\n", + (unsigned long)GC_n_rescuing_pages); +# endif + GC_push_roots(FALSE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + return(FALSE); + + case MS_PUSH_UNCOLLECTABLE: + if (GC_mark_stack_top + >= GC_mark_stack + INITIAL_MARK_STACK_SIZE/4) { + GC_mark_from_mark_stack(); + return(FALSE); + } else { + scan_ptr = GC_push_next_marked_uncollectable(scan_ptr); + if (scan_ptr == 0) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + } + return(FALSE); + + case MS_ROOTS_PUSHED: + if (GC_mark_stack_top >= GC_mark_stack) { + GC_mark_from_mark_stack(); + return(FALSE); + } else { + GC_mark_state = MS_NONE; + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + return(TRUE); + } + + case MS_INVALID: + case MS_PARTIALLY_INVALID: + if (!GC_objects_are_marked) { + GC_mark_state = MS_PUSH_UNCOLLECTABLE; + return(FALSE); + } + if (GC_mark_stack_top >= GC_mark_stack) { + GC_mark_from_mark_stack(); + return(FALSE); + } + if (scan_ptr == 0 && GC_mark_state == MS_INVALID) { + /* About to start a heap scan for marked objects. */ + /* Mark stack is empty. OK to reallocate. */ + if (GC_mark_stack_too_small) { + alloc_mark_stack(2*GC_mark_stack_size); + } + GC_mark_state = MS_PARTIALLY_INVALID; + } + scan_ptr = GC_push_next_marked(scan_ptr); + if (scan_ptr == 0 && GC_mark_state == MS_PARTIALLY_INVALID) { + GC_push_roots(TRUE, cold_gc_frame); + GC_objects_are_marked = TRUE; + if (GC_mark_state != MS_INVALID) { + GC_mark_state = MS_ROOTS_PUSHED; + } + } + return(FALSE); + default: + ABORT("GC_mark_some: bad state"); + return(FALSE); + } +} + + +GC_bool GC_mark_stack_empty() +{ + return(GC_mark_stack_top < GC_mark_stack); +} + +#ifdef PROF_MARKER + word GC_prof_array[10]; +# define PROF(n) GC_prof_array[n]++ +#else +# define PROF(n) +#endif + +/* Given a pointer to someplace other than a small object page or the */ +/* first page of a large object, return a pointer either to the */ +/* start of the large object or NIL. */ +/* In the latter case black list the address current. */ +/* Returns NIL without black listing if current points to a block */ +/* with IGNORE_OFF_PAGE set. */ +/*ARGSUSED*/ +# ifdef PRINT_BLACK_LIST + word GC_find_start(current, hhdr, source) + word source; +# else + word GC_find_start(current, hhdr) +# define source 0 +# endif +register word current; +register hdr * hhdr; +{ +# ifdef ALL_INTERIOR_POINTERS + if (hhdr != 0) { + register word orig = current; + + current = (word)HBLKPTR(current) + HDR_BYTES; + do { + current = current - HBLKSIZE*(word)hhdr; + hhdr = HDR(current); + } while(IS_FORWARDING_ADDR_OR_NIL(hhdr)); + /* current points to the start of the large object */ + if (hhdr -> hb_flags & IGNORE_OFF_PAGE) return(0); + if ((word *)orig - (word *)current + >= (ptrdiff_t)(hhdr->hb_sz)) { + /* Pointer past the end of the block */ + GC_ADD_TO_BLACK_LIST_NORMAL(orig, source); + return(0); + } + return(current); + } else { + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + return(0); + } +# else + GC_ADD_TO_BLACK_LIST_NORMAL(current, source); + return(0); +# endif +# undef source +} + +void GC_invalidate_mark_state() +{ + GC_mark_state = MS_INVALID; + GC_mark_stack_top = GC_mark_stack-1; +} + +mse * GC_signal_mark_stack_overflow(msp) +mse * msp; +{ + GC_mark_state = MS_INVALID; + GC_mark_stack_too_small = TRUE; +# ifdef PRINTSTATS + GC_printf1("Mark stack overflow; current size = %lu entries\n", + GC_mark_stack_size); +# endif + return(msp-INITIAL_MARK_STACK_SIZE/8); +} + + +/* + * Mark objects pointed to by the regions described by + * mark stack entries between GC_mark_stack and GC_mark_stack_top, + * inclusive. Assumes the upper limit of a mark stack entry + * is never 0. A mark stack entry never has size 0. + * We try to traverse on the order of a hblk of memory before we return. + * Caller is responsible for calling this until the mark stack is empty. + */ +void GC_mark_from_mark_stack() +{ + mse * GC_mark_stack_reg = GC_mark_stack; + mse * GC_mark_stack_top_reg = GC_mark_stack_top; + mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); + int credit = HBLKSIZE; /* Remaining credit for marking work */ + register word * current_p; /* Pointer to current candidate ptr. */ + register word current; /* Candidate pointer. */ + register word * limit; /* (Incl) limit of current candidate */ + /* range */ + register word descr; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; +# define SPLIT_RANGE_WORDS 128 /* Must be power of 2. */ + + GC_objects_are_marked = TRUE; +# ifdef OS2 /* Use untweaked version to circumvent compiler problem */ + while (GC_mark_stack_top_reg >= GC_mark_stack_reg && credit >= 0) { +# else + while ((((ptr_t)GC_mark_stack_top_reg - (ptr_t)GC_mark_stack_reg) | credit) + >= 0) { +# endif + current_p = GC_mark_stack_top_reg -> mse_start; + retry: + descr = GC_mark_stack_top_reg -> mse_descr; + if (descr & ((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS) - 1)) | DS_TAGS)) { + word tag = descr & DS_TAGS; + + switch(tag) { + case DS_LENGTH: + /* Large length. */ + /* Process part of the range to avoid pushing too much on the */ + /* stack. */ + GC_mark_stack_top_reg -> mse_start = + limit = current_p + SPLIT_RANGE_WORDS-1; + GC_mark_stack_top_reg -> mse_descr -= + WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); + /* Make sure that pointers overlapping the two ranges are */ + /* considered. */ + limit = (word *)((char *)limit + sizeof(word) - ALIGNMENT); + break; + case DS_BITMAP: + GC_mark_stack_top_reg--; + descr &= ~DS_TAGS; + credit -= WORDS_TO_BYTES(WORDSZ/2); /* guess */ + while (descr != 0) { + if ((signed_word)descr < 0) { + current = *current_p; + if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { + PUSH_CONTENTS(current, GC_mark_stack_top_reg, mark_stack_limit, + current_p, exit1); + } + } + descr <<= 1; + ++ current_p; + } + continue; + case DS_PROC: + GC_mark_stack_top_reg--; + credit -= PROC_BYTES; + GC_mark_stack_top_reg = + (*PROC(descr)) + (current_p, GC_mark_stack_top_reg, + mark_stack_limit, ENV(descr)); + continue; + case DS_PER_OBJECT: + GC_mark_stack_top_reg -> mse_descr = + *(word *)((ptr_t)current_p + descr - tag); + goto retry; + } + } else { + GC_mark_stack_top_reg--; + limit = (word *)(((ptr_t)current_p) + (word)descr); + } + /* The simple case in which we're scanning a range. */ + credit -= (ptr_t)limit - (ptr_t)current_p; + limit -= 1; + while (current_p <= limit) { + current = *current_p; + if ((ptr_t)current >= least_ha && (ptr_t)current < greatest_ha) { + PUSH_CONTENTS(current, GC_mark_stack_top_reg, + mark_stack_limit, current_p, exit2); + } + current_p = (word *)((char *)current_p + ALIGNMENT); + } + } + GC_mark_stack_top = GC_mark_stack_top_reg; +} + +/* Allocate or reallocate space for mark stack of size s words */ +/* May silently fail. */ +static void alloc_mark_stack(n) +word n; +{ + mse * new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct ms_entry)); + + GC_mark_stack_too_small = FALSE; + if (GC_mark_stack_size != 0) { + if (new_stack != 0) { + word displ = (word)GC_mark_stack & (GC_page_size - 1); + signed_word size = GC_mark_stack_size * sizeof(struct ms_entry); + + /* Recycle old space */ + if (0 != displ) displ = GC_page_size - displ; + size = (size - displ) & ~(GC_page_size - 1); + if (size > 0) { + GC_add_to_heap((struct hblk *) + ((word)GC_mark_stack + displ), (word)size); + } + GC_mark_stack = new_stack; + GC_mark_stack_size = n; +# ifdef PRINTSTATS + GC_printf1("Grew mark stack to %lu frames\n", + (unsigned long) GC_mark_stack_size); +# endif + } else { +# ifdef PRINTSTATS + GC_printf1("Failed to grow mark stack to %lu frames\n", + (unsigned long) n); +# endif + } + } else { + if (new_stack == 0) { + GC_err_printf0("No space for mark stack\n"); + EXIT(); + } + GC_mark_stack = new_stack; + GC_mark_stack_size = n; + } + GC_mark_stack_top = GC_mark_stack-1; +} + +void GC_mark_init() +{ + alloc_mark_stack(INITIAL_MARK_STACK_SIZE); +} + +/* + * Push all locations between b and t onto the mark stack. + * b is the first location to be checked. t is one past the last + * location to be checked. + * Should only be used if there is no possibility of mark stack + * overflow. + */ +void GC_push_all(bottom, top) +ptr_t bottom; +ptr_t top; +{ + register word length; + + bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (ptr_t)(((word) top) & ~(ALIGNMENT-1)); + if (top == 0 || bottom == top) return; + GC_mark_stack_top++; + if (GC_mark_stack_top >= GC_mark_stack + GC_mark_stack_size) { + ABORT("unexpected mark stack overflow"); + } + length = top - bottom; +# if DS_TAGS > ALIGNMENT - 1 + length += DS_TAGS; + length &= ~DS_TAGS; +# endif + GC_mark_stack_top -> mse_start = (word *)bottom; + GC_mark_stack_top -> mse_descr = length; +} + +/* + * Analogous to the above, but push only those pages that may have been + * dirtied. A block h is assumed dirty if dirty_fn(h) != 0. + * We use push_fn to actually push the block. + * Will not overflow mark stack if push_fn pushes a small fixed number + * of entries. (This is invoked only if push_fn pushes a single entry, + * or if it marks each object before pushing it, thus ensuring progress + * in the event of a stack overflow.) + */ +void GC_push_dirty(bottom, top, dirty_fn, push_fn) +ptr_t bottom; +ptr_t top; +int (*dirty_fn)(/* struct hblk * h */); +void (*push_fn)(/* ptr_t bottom, ptr_t top */); +{ + register struct hblk * h; + + bottom = (ptr_t)(((long) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + top = (ptr_t)(((long) top) & ~(ALIGNMENT-1)); + + if (top == 0 || bottom == top) return; + h = HBLKPTR(bottom + HBLKSIZE); + if (top <= (ptr_t) h) { + if ((*dirty_fn)(h-1)) { + (*push_fn)(bottom, top); + } + return; + } + if ((*dirty_fn)(h-1)) { + (*push_fn)(bottom, (ptr_t)h); + } + while ((ptr_t)(h+1) <= top) { + if ((*dirty_fn)(h)) { + if ((word)(GC_mark_stack_top - GC_mark_stack) + > 3 * GC_mark_stack_size / 4) { + /* Danger of mark stack overflow */ + (*push_fn)((ptr_t)h, top); + return; + } else { + (*push_fn)((ptr_t)h, (ptr_t)(h+1)); + } + } + h++; + } + if ((ptr_t)h != top) { + if ((*dirty_fn)(h)) { + (*push_fn)((ptr_t)h, top); + } + } + if (GC_mark_stack_top >= GC_mark_stack + GC_mark_stack_size) { + ABORT("unexpected mark stack overflow"); + } +} + +# ifndef SMALL_CONFIG +void GC_push_conditional(bottom, top, all) +ptr_t bottom; +ptr_t top; +int all; +{ + if (all) { + if (GC_dirty_maintained) { +# ifdef PROC_VDB + /* Pages that were never dirtied cannot contain pointers */ + GC_push_dirty(bottom, top, GC_page_was_ever_dirty, GC_push_all); +# else + GC_push_all(bottom, top); +# endif + } else { + GC_push_all(bottom, top); + } + } else { + GC_push_dirty(bottom, top, GC_page_was_dirty, GC_push_all); + } +} +#endif + +# ifdef MSWIN32 + void __cdecl GC_push_one(p) +# else + void GC_push_one(p) +# endif +word p; +{ + GC_PUSH_ONE_STACK(p, 0); +} + +# ifdef __STDC__ +# define BASE(p) (word)GC_base((void *)(p)) +# else +# define BASE(p) (word)GC_base((char *)(p)) +# endif + +/* As above, but argument passed preliminary test. */ +# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS) + void GC_push_one_checked(p, interior_ptrs, source) + ptr_t source; +# else + void GC_push_one_checked(p, interior_ptrs) +# define source 0 +# endif +register word p; +register GC_bool interior_ptrs; +{ + register word r; + register hdr * hhdr; + register int displ; + + GET_HDR(p, hhdr); + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + if (hhdr != 0 && interior_ptrs) { + r = BASE(p); + hhdr = HDR(r); + displ = BYTES_TO_WORDS(HBLKDISPL(r)); + } else { + hhdr = 0; + } + } else { + register map_entry_type map_entry; + + displ = HBLKDISPL(p); + map_entry = MAP_ENTRY((hhdr -> hb_map), displ); + if (map_entry == OBJ_INVALID) { +# ifndef ALL_INTERIOR_POINTERS + if (interior_ptrs) { + r = BASE(p); + displ = BYTES_TO_WORDS(HBLKDISPL(r)); + if (r == 0) hhdr = 0; + } else { + hhdr = 0; + } +# else + /* map already reflects interior pointers */ + hhdr = 0; +# endif + } else { + displ = BYTES_TO_WORDS(displ); + displ -= map_entry; + r = (word)((word *)(HBLKPTR(p)) + displ); + } + } + /* If hhdr != 0 then r == GC_base(p), only we did it faster. */ + /* displ is the word index within the block. */ + if (hhdr == 0) { + if (interior_ptrs) { +# ifdef PRINT_BLACK_LIST + GC_add_to_black_list_stack(p, source); +# else + GC_add_to_black_list_stack(p); +# endif + } else { + GC_ADD_TO_BLACK_LIST_NORMAL(p, source); +# undef source /* In case we had to define it. */ + } + } else { + if (!mark_bit_from_hdr(hhdr, displ)) { + set_mark_bit_from_hdr(hhdr, displ); + GC_STORE_BACK_PTR(source, (ptr_t)r); + PUSH_OBJ((word *)r, hhdr, GC_mark_stack_top, + &(GC_mark_stack[GC_mark_stack_size])); + } + } +} + +# ifdef TRACE_BUF + +# define TRACE_ENTRIES 1000 + +struct trace_entry { + char * kind; + word gc_no; + word words_allocd; + word arg1; + word arg2; +} GC_trace_buf[TRACE_ENTRIES]; + +int GC_trace_buf_ptr = 0; + +void GC_add_trace_entry(char *kind, word arg1, word arg2) +{ + GC_trace_buf[GC_trace_buf_ptr].kind = kind; + GC_trace_buf[GC_trace_buf_ptr].gc_no = GC_gc_no; + GC_trace_buf[GC_trace_buf_ptr].words_allocd = GC_words_allocd; + GC_trace_buf[GC_trace_buf_ptr].arg1 = arg1 ^ 0x80000000; + GC_trace_buf[GC_trace_buf_ptr].arg2 = arg2 ^ 0x80000000; + GC_trace_buf_ptr++; + if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0; +} + +void GC_print_trace(word gc_no, GC_bool lock) +{ + int i; + struct trace_entry *p; + + if (lock) LOCK(); + for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) { + if (i < 0) i = TRACE_ENTRIES-1; + p = GC_trace_buf + i; + if (p -> gc_no < gc_no || p -> kind == 0) return; + printf("Trace:%s (gc:%d,words:%d) 0x%X, 0x%X\n", + p -> kind, p -> gc_no, p -> words_allocd, + (p -> arg1) ^ 0x80000000, (p -> arg2) ^ 0x80000000); + } + printf("Trace incomplete\n"); + if (lock) UNLOCK(); +} + +# endif /* TRACE_BUF */ + +/* + * A version of GC_push_all that treats all interior pointers as valid + * and scans the entire region immediately, in case the contents + * change. + */ +void GC_push_all_eager(bottom, top) +ptr_t bottom; +ptr_t top; +{ + word * b = (word *)(((long) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1)); + word * t = (word *)(((long) top) & ~(ALIGNMENT-1)); + register word *p; + register word q; + register word *lim; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + if (top == 0) return; + /* check all pointers in range and put in push if they appear */ + /* to be valid. */ + lim = t - 1 /* longword */; + for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) { + q = *p; + GC_PUSH_ONE_STACK(q, p); + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +#ifndef THREADS +/* + * A version of GC_push_all that treats all interior pointers as valid + * and scans part of the area immediately, to make sure that saved + * register values are not lost. + * Cold_gc_frame delimits the stack section that must be scanned + * eagerly. A zero value indicates that no eager scanning is needed. + */ +void GC_push_all_stack_partially_eager(bottom, top, cold_gc_frame) +ptr_t bottom; +ptr_t top; +ptr_t cold_gc_frame; +{ +# ifdef ALL_INTERIOR_POINTERS +# define EAGER_BYTES 1024 + /* Push the hot end of the stack eagerly, so that register values */ + /* saved inside GC frames are marked before they disappear. */ + /* The rest of the marking can be deferred until later. */ + if (0 == cold_gc_frame) { + GC_push_all_stack(bottom, top); + return; + } +# ifdef STACK_GROWS_DOWN + GC_push_all_eager(bottom, cold_gc_frame); + GC_push_all(cold_gc_frame - sizeof(ptr_t), top); +# else /* STACK_GROWS_UP */ + GC_push_all_eager(cold_gc_frame, top); + GC_push_all(bottom, cold_gc_frame + sizeof(ptr_t)); +# endif /* STACK_GROWS_UP */ +# else + GC_push_all_eager(bottom, top); +# endif +# ifdef TRACE_BUF + GC_add_trace_entry("GC_push_all_stack", bottom, top); +# endif +} +#endif /* !THREADS */ + +void GC_push_all_stack(bottom, top) +ptr_t bottom; +ptr_t top; +{ +# ifdef ALL_INTERIOR_POINTERS + GC_push_all(bottom, top); +# else + GC_push_all_eager(bottom, top); +# endif +} + +#ifndef SMALL_CONFIG +/* Push all objects reachable from marked objects in the given block */ +/* of size 1 objects. */ +void GC_push_marked1(h, hhdr) +struct hblk *h; +register hdr * hhdr; +{ + word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p; + word *plim; + register int i; + register word q; + register word mark_word; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + i = 0; + while(mark_word != 0) { + if (mark_word & 1) { + q = p[i]; + GC_PUSH_ONE_HEAP(q, p + i); + } + i++; + mark_word >>= 1; + } + p += WORDSZ; + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + + +#ifndef UNALIGNED + +/* Push all objects reachable from marked objects in the given block */ +/* of size 2 objects. */ +void GC_push_marked2(h, hhdr) +struct hblk *h; +register hdr * hhdr; +{ + word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p; + word *plim; + register int i; + register word q; + register word mark_word; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + i = 0; + while(mark_word != 0) { + if (mark_word & 1) { + q = p[i]; + GC_PUSH_ONE_HEAP(q, p + i); + q = p[i+1]; + GC_PUSH_ONE_HEAP(q, p + i); + } + i += 2; + mark_word >>= 2; + } + p += WORDSZ; + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +/* Push all objects reachable from marked objects in the given block */ +/* of size 4 objects. */ +/* There is a risk of mark stack overflow here. But we handle that. */ +/* And only unmarked objects get pushed, so it's not very likely. */ +void GC_push_marked4(h, hhdr) +struct hblk *h; +register hdr * hhdr; +{ + word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p; + word *plim; + register int i; + register word q; + register word mark_word; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; +# define GC_greatest_plausible_heap_addr greatest_ha +# define GC_least_plausible_heap_addr least_ha + + p = (word *)(h->hb_body); + plim = (word *)(((word)h) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + i = 0; + while(mark_word != 0) { + if (mark_word & 1) { + q = p[i]; + GC_PUSH_ONE_HEAP(q, p + i); + q = p[i+1]; + GC_PUSH_ONE_HEAP(q, p + i + 1); + q = p[i+2]; + GC_PUSH_ONE_HEAP(q, p + i + 2); + q = p[i+3]; + GC_PUSH_ONE_HEAP(q, p + i + 3); + } + i += 4; + mark_word >>= 4; + } + p += WORDSZ; + } +# undef GC_greatest_plausible_heap_addr +# undef GC_least_plausible_heap_addr +} + +#endif /* UNALIGNED */ + +#endif /* SMALL_CONFIG */ + +/* Push all objects reachable from marked objects in the given block */ +void GC_push_marked(h, hhdr) +struct hblk *h; +register hdr * hhdr; +{ + register int sz = hhdr -> hb_sz; + register word * p; + register int word_no; + register word * lim; + register mse * GC_mark_stack_top_reg; + register mse * mark_stack_limit = &(GC_mark_stack[GC_mark_stack_size]); + + /* Some quick shortcuts: */ + { + struct obj_kind *ok = &(GC_obj_kinds[hhdr -> hb_obj_kind]); + if ((0 | DS_LENGTH) == ok -> ok_descriptor + && FALSE == ok -> ok_relocate_descr) + return; + } + if (GC_block_empty(hhdr)/* nothing marked */) return; +# ifdef GATHERSTATS + GC_n_rescuing_pages++; +# endif + GC_objects_are_marked = TRUE; + if (sz > MAXOBJSZ) { + lim = (word *)(h + 1); + } else { + lim = (word *)(h + 1) - sz; + } + + switch(sz) { +# if !defined(SMALL_CONFIG) + case 1: + GC_push_marked1(h, hhdr); + break; +# endif +# if !defined(SMALL_CONFIG) && !defined(UNALIGNED) + case 2: + GC_push_marked2(h, hhdr); + break; + case 4: + GC_push_marked4(h, hhdr); + break; +# endif + default: + GC_mark_stack_top_reg = GC_mark_stack_top; + for (p = (word *)h + HDR_WORDS, word_no = HDR_WORDS; p <= lim; + p += sz, word_no += sz) { + /* This ignores user specified mark procs. This currently */ + /* doesn't matter, since marking from the whole object */ + /* is always sufficient, and we will eventually use the user */ + /* mark proc to avoid any bogus pointers. */ + if (mark_bit_from_hdr(hhdr, word_no)) { + /* Mark from fields inside the object */ + PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top_reg, mark_stack_limit); +# ifdef GATHERSTATS + /* Subtract this object from total, since it was */ + /* added in twice. */ + GC_composite_in_use -= sz; +# endif + } + } + GC_mark_stack_top = GC_mark_stack_top_reg; + } +} + +#ifndef SMALL_CONFIG +/* Test whether any page in the given block is dirty */ +GC_bool GC_block_was_dirty(h, hhdr) +struct hblk *h; +register hdr * hhdr; +{ + register int sz = hhdr -> hb_sz; + + if (sz < MAXOBJSZ) { + return(GC_page_was_dirty(h)); + } else { + register ptr_t p = (ptr_t)h; + sz += HDR_WORDS; + sz = WORDS_TO_BYTES(sz); + while (p < (ptr_t)h + sz) { + if (GC_page_was_dirty((struct hblk *)p)) return(TRUE); + p += HBLKSIZE; + } + return(FALSE); + } +} +#endif /* SMALL_CONFIG */ + +/* Similar to GC_push_next_marked, but return address of next block */ +struct hblk * GC_push_next_marked(h) +struct hblk *h; +{ + register hdr * hhdr; + + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = HDR(h); + GC_push_marked(h, hhdr); + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} + +#ifndef SMALL_CONFIG +/* Identical to above, but mark only from dirty pages */ +struct hblk * GC_push_next_marked_dirty(h) +struct hblk *h; +{ + register hdr * hhdr = HDR(h); + + if (!GC_dirty_maintained) { ABORT("dirty bits not set up"); } + for (;;) { + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = HDR(h); +# ifdef STUBBORN_ALLOC + if (hhdr -> hb_obj_kind == STUBBORN) { + if (GC_page_was_changed(h) && GC_block_was_dirty(h, hhdr)) { + break; + } + } else { + if (GC_block_was_dirty(h, hhdr)) break; + } +# else + if (GC_block_was_dirty(h, hhdr)) break; +# endif + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + GC_push_marked(h, hhdr); + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} +#endif + +/* Similar to above, but for uncollectable pages. Needed since we */ +/* do not clear marks for such pages, even for full collections. */ +struct hblk * GC_push_next_marked_uncollectable(h) +struct hblk *h; +{ + register hdr * hhdr = HDR(h); + + for (;;) { + h = GC_next_used_block(h); + if (h == 0) return(0); + hhdr = HDR(h); + if (hhdr -> hb_obj_kind == UNCOLLECTABLE) break; + h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz); + } + GC_push_marked(h, hhdr); + return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz)); +} + + diff --git a/gc/mark_rts.c b/gc/mark_rts.c new file mode 100644 index 0000000..2f21ed3 --- /dev/null +++ b/gc/mark_rts.c @@ -0,0 +1,484 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, October 9, 1995 1:06 pm PDT */ +# include <stdio.h> +# include "gc_priv.h" + +/* Data structure for list of root sets. */ +/* We keep a hash table, so that we can filter out duplicate additions. */ +/* Under Win32, we need to do a better job of filtering overlaps, so */ +/* we resort to sequential search, and pay the price. */ +/* This is really declared in gc_priv.h: +struct roots { + ptr_t r_start; + ptr_t r_end; + # ifndef MSWIN32 + struct roots * r_next; + # endif + GC_bool r_tmp; + -- Delete before registering new dynamic libraries +}; + +struct roots GC_static_roots[MAX_ROOT_SETS]; +*/ + +static int n_root_sets = 0; + + /* GC_static_roots[0..n_root_sets) contains the valid root sets. */ + +# if !defined(NO_DEBUGGING) +/* For debugging: */ +void GC_print_static_roots() +{ + register int i; + size_t total = 0; + + for (i = 0; i < n_root_sets; i++) { + GC_printf2("From 0x%lx to 0x%lx ", + (unsigned long) GC_static_roots[i].r_start, + (unsigned long) GC_static_roots[i].r_end); + if (GC_static_roots[i].r_tmp) { + GC_printf0(" (temporary)\n"); + } else { + GC_printf0("\n"); + } + total += GC_static_roots[i].r_end - GC_static_roots[i].r_start; + } + GC_printf1("Total size: %ld\n", (unsigned long) total); + if (GC_root_size != total) { + GC_printf1("GC_root_size incorrect: %ld!!\n", + (unsigned long) GC_root_size); + } +} +# endif /* NO_DEBUGGING */ + +/* Primarily for debugging support: */ +/* Is the address p in one of the registered static */ +/* root sections? */ +GC_bool GC_is_static_root(p) +ptr_t p; +{ + static int last_root_set = 0; + register int i; + + + if (p >= GC_static_roots[last_root_set].r_start + && p < GC_static_roots[last_root_set].r_end) return(TRUE); + for (i = 0; i < n_root_sets; i++) { + if (p >= GC_static_roots[i].r_start + && p < GC_static_roots[i].r_end) { + last_root_set = i; + return(TRUE); + } + } + return(FALSE); +} + +#ifndef MSWIN32 +/* +# define LOG_RT_SIZE 6 +# define RT_SIZE (1 << LOG_RT_SIZE) -- Power of 2, may be != MAX_ROOT_SETS + + struct roots * GC_root_index[RT_SIZE]; + -- Hash table header. Used only to check whether a range is + -- already present. + -- really defined in gc_priv.h +*/ + +static int rt_hash(addr) +char * addr; +{ + word result = (word) addr; +# if CPP_WORDSZ > 8*LOG_RT_SIZE + result ^= result >> 8*LOG_RT_SIZE; +# endif +# if CPP_WORDSZ > 4*LOG_RT_SIZE + result ^= result >> 4*LOG_RT_SIZE; +# endif + result ^= result >> 2*LOG_RT_SIZE; + result ^= result >> LOG_RT_SIZE; + result &= (RT_SIZE-1); + return(result); +} + +/* Is a range starting at b already in the table? If so return a */ +/* pointer to it, else NIL. */ +struct roots * GC_roots_present(b) +char *b; +{ + register int h = rt_hash(b); + register struct roots *p = GC_root_index[h]; + + while (p != 0) { + if (p -> r_start == (ptr_t)b) return(p); + p = p -> r_next; + } + return(FALSE); +} + +/* Add the given root structure to the index. */ +static void add_roots_to_index(p) +struct roots *p; +{ + register int h = rt_hash(p -> r_start); + + p -> r_next = GC_root_index[h]; + GC_root_index[h] = p; +} + +# else /* MSWIN32 */ + +# define add_roots_to_index(p) + +# endif + + + + +word GC_root_size = 0; + +void GC_add_roots(b, e) +char * b; char * e; +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + GC_add_roots_inner(b, e, FALSE); + UNLOCK(); + ENABLE_SIGNALS(); +} + + +/* Add [b,e) to the root set. Adding the same interval a second time */ +/* is a moderately fast noop, and hence benign. We do not handle */ +/* different but overlapping intervals efficiently. (We do handle */ +/* them correctly.) */ +/* Tmp specifies that the interval may be deleted before */ +/* reregistering dynamic libraries. */ +void GC_add_roots_inner(b, e, tmp) +char * b; char * e; +GC_bool tmp; +{ + struct roots * old; + +# ifdef MSWIN32 + /* Spend the time to ensure that there are no overlapping */ + /* or adjacent intervals. */ + /* This could be done faster with e.g. a */ + /* balanced tree. But the execution time here is */ + /* virtually guaranteed to be dominated by the time it */ + /* takes to scan the roots. */ + { + register int i; + + for (i = 0; i < n_root_sets; i++) { + old = GC_static_roots + i; + if ((ptr_t)b <= old -> r_end && (ptr_t)e >= old -> r_start) { + if ((ptr_t)b < old -> r_start) { + old -> r_start = (ptr_t)b; + GC_root_size += (old -> r_start - (ptr_t)b); + } + if ((ptr_t)e > old -> r_end) { + old -> r_end = (ptr_t)e; + GC_root_size += ((ptr_t)e - old -> r_end); + } + old -> r_tmp &= tmp; + break; + } + } + if (i < n_root_sets) { + /* merge other overlapping intervals */ + struct roots *other; + + for (i++; i < n_root_sets; i++) { + other = GC_static_roots + i; + b = (char *)(other -> r_start); + e = (char *)(other -> r_end); + if ((ptr_t)b <= old -> r_end && (ptr_t)e >= old -> r_start) { + if ((ptr_t)b < old -> r_start) { + old -> r_start = (ptr_t)b; + GC_root_size += (old -> r_start - (ptr_t)b); + } + if ((ptr_t)e > old -> r_end) { + old -> r_end = (ptr_t)e; + GC_root_size += ((ptr_t)e - old -> r_end); + } + old -> r_tmp &= other -> r_tmp; + /* Delete this entry. */ + GC_root_size -= (other -> r_end - other -> r_start); + other -> r_start = GC_static_roots[n_root_sets-1].r_start; + other -> r_end = GC_static_roots[n_root_sets-1].r_end; + n_root_sets--; + } + } + return; + } + } +# else + old = GC_roots_present(b); + if (old != 0) { + if ((ptr_t)e <= old -> r_end) /* already there */ return; + /* else extend */ + GC_root_size += (ptr_t)e - old -> r_end; + old -> r_end = (ptr_t)e; + return; + } +# endif + if (n_root_sets == MAX_ROOT_SETS) { + ABORT("Too many root sets\n"); + } + GC_static_roots[n_root_sets].r_start = (ptr_t)b; + GC_static_roots[n_root_sets].r_end = (ptr_t)e; + GC_static_roots[n_root_sets].r_tmp = tmp; +# ifndef MSWIN32 + GC_static_roots[n_root_sets].r_next = 0; +# endif + add_roots_to_index(GC_static_roots + n_root_sets); + GC_root_size += (ptr_t)e - (ptr_t)b; + n_root_sets++; +} + +void GC_clear_roots GC_PROTO((void)) +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + n_root_sets = 0; + GC_root_size = 0; +# ifndef MSWIN32 + { + register int i; + + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + } +# endif + UNLOCK(); + ENABLE_SIGNALS(); +} + +/* Internal use only; lock held. */ +void GC_remove_tmp_roots() +{ + register int i; + + for (i = 0; i < n_root_sets; ) { + if (GC_static_roots[i].r_tmp) { + GC_root_size -= + (GC_static_roots[i].r_end - GC_static_roots[i].r_start); + GC_static_roots[i].r_start = GC_static_roots[n_root_sets-1].r_start; + GC_static_roots[i].r_end = GC_static_roots[n_root_sets-1].r_end; + GC_static_roots[i].r_tmp = GC_static_roots[n_root_sets-1].r_tmp; + n_root_sets--; + } else { + i++; + } + } +# ifndef MSWIN32 + { + register int i; + + for (i = 0; i < RT_SIZE; i++) GC_root_index[i] = 0; + for (i = 0; i < n_root_sets; i++) + add_roots_to_index(GC_static_roots + i); + } +# endif + +} + +ptr_t GC_approx_sp() +{ + word dummy; + + return((ptr_t)(&dummy)); +} + +/* + * Data structure for excluded static roots. + * Real declaration is in gc_priv.h. + +struct exclusion { + ptr_t e_start; + ptr_t e_end; +}; + +struct exclusion GC_excl_table[MAX_EXCLUSIONS]; + -- Array of exclusions, ascending + -- address order. +*/ + +size_t GC_excl_table_entries = 0; /* Number of entries in use. */ + +/* Return the first exclusion range that includes an address >= start_addr */ +/* Assumes the exclusion table contains at least one entry (namely the */ +/* GC data structures). */ +struct exclusion * GC_next_exclusion(start_addr) +ptr_t start_addr; +{ + size_t low = 0; + size_t high = GC_excl_table_entries - 1; + size_t mid; + + while (high > low) { + mid = (low + high) >> 1; + /* low <= mid < high */ + if ((word) GC_excl_table[mid].e_end <= (word) start_addr) { + low = mid + 1; + } else { + high = mid; + } + } + if ((word) GC_excl_table[low].e_end <= (word) start_addr) return 0; + return GC_excl_table + low; +} + +void GC_exclude_static_roots(start, finish) +GC_PTR start; +GC_PTR finish; +{ + struct exclusion * next; + size_t next_index, i; + + if (0 == GC_excl_table_entries) { + next = 0; + } else { + next = GC_next_exclusion(start); + } + if (0 != next) { + if ((word)(next -> e_start) < (word) finish) { + /* incomplete error check. */ + ABORT("exclusion ranges overlap"); + } + if ((word)(next -> e_start) == (word) finish) { + /* extend old range backwards */ + next -> e_start = (ptr_t)start; + return; + } + next_index = next - GC_excl_table; + for (i = GC_excl_table_entries; i > next_index; --i) { + GC_excl_table[i] = GC_excl_table[i-1]; + } + } else { + next_index = GC_excl_table_entries; + } + if (GC_excl_table_entries == MAX_EXCLUSIONS) ABORT("Too many exclusions"); + GC_excl_table[next_index].e_start = (ptr_t)start; + GC_excl_table[next_index].e_end = (ptr_t)finish; + ++GC_excl_table_entries; +} + +/* Invoke push_conditional on ranges that are not excluded. */ +void GC_push_conditional_with_exclusions(bottom, top, all) +ptr_t bottom; +ptr_t top; +int all; +{ + struct exclusion * next; + ptr_t excl_start; + + while (bottom < top) { + next = GC_next_exclusion(bottom); + if (0 == next || (excl_start = next -> e_start) >= top) { + GC_push_conditional(bottom, top, all); + return; + } + if (excl_start > bottom) GC_push_conditional(bottom, excl_start, all); + bottom = next -> e_end; + } +} + +/* + * In the absence of threads, push the stack contents. + * In the presence of threads, push enough of the current stack + * to ensure that callee-save registers saved in collector frames have been + * seen. + */ +void GC_push_current_stack(cold_gc_frame) +ptr_t cold_gc_frame; +{ +# if defined(THREADS) + if (0 == cold_gc_frame) return; +# ifdef STACK_GROWS_DOWN + GC_push_all_eager(GC_approx_sp(), cold_gc_frame); +# else + GC_push_all_eager( cold_gc_frame, GC_approx_sp() ); +# endif +# else +# ifdef STACK_GROWS_DOWN + GC_push_all_stack_partially_eager( GC_approx_sp(), GC_stackbottom, + cold_gc_frame ); +# else + GC_push_all_stack_partially_eager( GC_stackbottom, GC_approx_sp(), + cold_gc_frame ); +# endif +# endif /* !THREADS */ +} + +/* + * Call the mark routines (GC_tl_push for a single pointer, GC_push_conditional + * on groups of pointers) on every top level accessible pointer. + * If all is FALSE, arrange to push only possibly altered values. + * Cold_gc_frame is an address inside a GC frame that + * remains valid until all marking is complete. + * A zero value indicates that it's OK to miss some + * register values. + */ +void GC_push_roots(all, cold_gc_frame) +GC_bool all; +ptr_t cold_gc_frame; +{ + register int i; + + /* + * push registers - i.e., call GC_push_one(r) for each + * register contents r. + */ +# ifdef USE_GENERIC_PUSH_REGS + GC_generic_push_regs(cold_gc_frame); +# else + GC_push_regs(); /* usually defined in machine_dep.c */ +# endif + + /* + * Next push static data. This must happen early on, since it's + * not robust against mark stack overflow. + */ + /* Reregister dynamic libraries, in case one got added. */ +# if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(PCR)) \ + && !defined(SRC_M3) + GC_remove_tmp_roots(); + GC_register_dynamic_libraries(); +# endif + /* Mark everything in static data areas */ + for (i = 0; i < n_root_sets; i++) { + GC_push_conditional_with_exclusions( + GC_static_roots[i].r_start, + GC_static_roots[i].r_end, all); + } + + /* + * Now traverse stacks. + */ +# if !defined(USE_GENERIC_PUSH_REGS) + GC_push_current_stack(cold_gc_frame); + /* IN the threads case, this only pushes collector frames. */ + /* In the USE_GENERIC_PUSH_REGS case, this is done inside */ + /* GC_push_regs, so that we catch callee-save registers saved */ + /* inside the GC_push_regs frame. */ +# endif + if (GC_push_other_roots != 0) (*GC_push_other_roots)(); + /* In the threads case, this also pushes thread stacks. */ +} + diff --git a/gc/mips_sgi_mach_dep.s b/gc/mips_sgi_mach_dep.s new file mode 100644 index 0000000..03c4b98 --- /dev/null +++ b/gc/mips_sgi_mach_dep.s @@ -0,0 +1,40 @@ +#include <sys/regdef.h> +#include <sys/asm.h> + +# define call_push(x) move $4,x; jal GC_push_one + + .text +/* Mark from machine registers that are saved by C compiler */ +# define FRAMESZ 32 +# define RAOFF FRAMESZ-SZREG +# define GPOFF FRAMESZ-(2*SZREG) + NESTED(GC_push_regs, FRAMESZ, ra) + .mask 0x80000000,-SZREG # inform debugger of saved ra loc + move t0,gp + SETUP_GPX(t8) + PTR_SUBU sp,FRAMESZ +# ifdef SETUP_GP64 + SETUP_GP64(GPOFF, GC_push_regs) +# endif + SAVE_GP(GPOFF) + REG_S ra,RAOFF(sp) +# if (_MIPS_SIM == _MIPS_SIM_ABI32) + call_push($2) + call_push($3) +# endif + call_push($16) + call_push($17) + call_push($18) + call_push($19) + call_push($20) + call_push($21) + call_push($22) + call_push($23) + call_push($30) + REG_L ra,RAOFF(sp) +# ifdef RESTORE_GP64 + RESTORE_GP64 +# endif + PTR_ADDU sp,FRAMESZ + j ra + .end GC_push_regs diff --git a/gc/mips_ultrix_mach_dep.s b/gc/mips_ultrix_mach_dep.s new file mode 100644 index 0000000..178224e --- /dev/null +++ b/gc/mips_ultrix_mach_dep.s @@ -0,0 +1,26 @@ +# define call_push(x) move $4,x; jal GC_push_one + + .text + # Mark from machine registers that are saved by C compiler + .globl GC_push_regs + .ent GC_push_regs +GC_push_regs: + subu $sp,8 ## Need to save only return address + sw $31,4($sp) + .mask 0x80000000,-4 + .frame $sp,8,$31 + call_push($2) + call_push($3) + call_push($16) + call_push($17) + call_push($18) + call_push($19) + call_push($20) + call_push($21) + call_push($22) + call_push($23) + call_push($30) + lw $31,4($sp) + addu $sp,8 + j $31 + .end GC_push_regs diff --git a/gc/misc.c b/gc/misc.c new file mode 100644 index 0000000..e0d4599 --- /dev/null +++ b/gc/misc.c @@ -0,0 +1,829 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, July 31, 1995 5:02 pm PDT */ + + +#include <stdio.h> +#include <signal.h> + +#define I_HIDE_POINTERS /* To make GC_call_with_alloc_lock visible */ +#include "gc_priv.h" + +#ifdef SOLARIS_THREADS +# include <sys/syscall.h> +#endif +#ifdef MSWIN32 +# include <windows.h> +#endif + +# ifdef THREADS +# ifdef PCR +# include "il/PCR_IL.h" + PCR_Th_ML GC_allocate_ml; +# else +# ifdef SRC_M3 + /* Critical section counter is defined in the M3 runtime */ + /* That's all we use. */ +# else +# ifdef SOLARIS_THREADS + mutex_t GC_allocate_ml; /* Implicitly initialized. */ +# else +# ifdef WIN32_THREADS + GC_API CRITICAL_SECTION GC_allocate_ml; +# else +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) +# ifdef UNDEFINED + pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; +# endif + pthread_t GC_lock_holder = NO_THREAD; +# else + --> declare allocator lock here +# endif +# endif +# endif +# endif +# endif +# endif + +GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; + + +GC_bool GC_debugging_started = FALSE; + /* defined here so we don't have to load debug_malloc.o */ + +void (*GC_check_heap)() = (void (*)())0; + +void (*GC_start_call_back)() = (void (*)())0; + +ptr_t GC_stackbottom = 0; + +GC_bool GC_dont_gc = 0; + +GC_bool GC_quiet = 0; + +#ifdef FIND_LEAK + int GC_find_leak = 1; +#else + int GC_find_leak = 0; +#endif + +/*ARGSUSED*/ +GC_PTR GC_default_oom_fn GC_PROTO((size_t bytes_requested)) +{ + return(0); +} + +GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn; + +extern signed_word GC_mem_found; + +# ifdef MERGE_SIZES + /* Set things up so that GC_size_map[i] >= words(i), */ + /* but not too much bigger */ + /* and so that size_map contains relatively few distinct entries */ + /* This is stolen from Russ Atkinson's Cedar quantization */ + /* alogrithm (but we precompute it). */ + + + void GC_init_size_map() + { + register unsigned i; + + /* Map size 0 to 1. This avoids problems at lower levels. */ + GC_size_map[0] = 1; + /* One word objects don't have to be 2 word aligned. */ + for (i = 1; i < sizeof(word); i++) { + GC_size_map[i] = 1; + } + GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word)); + for (i = sizeof(word) + 1; i <= 8 * sizeof(word); i++) { +# ifdef ALIGN_DOUBLE + GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1); +# else + GC_size_map[i] = ROUNDED_UP_WORDS(i); +# endif + } + for (i = 8*sizeof(word) + 1; i <= 16 * sizeof(word); i++) { + GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1); + } + /* We leave the rest of the array to be filled in on demand. */ + } + + /* Fill in additional entries in GC_size_map, including the ith one */ + /* We assume the ith entry is currently 0. */ + /* Note that a filled in section of the array ending at n always */ + /* has length at least n/4. */ + void GC_extend_size_map(i) + word i; + { + word orig_word_sz = ROUNDED_UP_WORDS(i); + word word_sz = orig_word_sz; + register word byte_sz = WORDS_TO_BYTES(word_sz); + /* The size we try to preserve. */ + /* Close to to i, unless this would */ + /* introduce too many distinct sizes. */ + word smaller_than_i = byte_sz - (byte_sz >> 3); + word much_smaller_than_i = byte_sz - (byte_sz >> 2); + register word low_limit; /* The lowest indexed entry we */ + /* initialize. */ + register word j; + + if (GC_size_map[smaller_than_i] == 0) { + low_limit = much_smaller_than_i; + while (GC_size_map[low_limit] != 0) low_limit++; + } else { + low_limit = smaller_than_i + 1; + while (GC_size_map[low_limit] != 0) low_limit++; + word_sz = ROUNDED_UP_WORDS(low_limit); + word_sz += word_sz >> 3; + if (word_sz < orig_word_sz) word_sz = orig_word_sz; + } +# ifdef ALIGN_DOUBLE + word_sz += 1; + word_sz &= ~1; +# endif + if (word_sz > MAXOBJSZ) { + word_sz = MAXOBJSZ; + } + /* If we can fit the same number of larger objects in a block, */ + /* do so. */ + { + size_t number_of_objs = BODY_SZ/word_sz; + word_sz = BODY_SZ/number_of_objs; +# ifdef ALIGN_DOUBLE + word_sz &= ~1; +# endif + } + byte_sz = WORDS_TO_BYTES(word_sz); +# ifdef ADD_BYTE_AT_END + /* We need one extra byte; don't fill in GC_size_map[byte_sz] */ + byte_sz--; +# endif + + for (j = low_limit; j <= byte_sz; j++) GC_size_map[j] = word_sz; + } +# endif + + +/* + * The following is a gross hack to deal with a problem that can occur + * on machines that are sloppy about stack frame sizes, notably SPARC. + * Bogus pointers may be written to the stack and not cleared for + * a LONG time, because they always fall into holes in stack frames + * that are not written. We partially address this by clearing + * sections of the stack whenever we get control. + */ +word GC_stack_last_cleared = 0; /* GC_no when we last did this */ +# ifdef THREADS +# define CLEAR_SIZE 2048 +# else +# define CLEAR_SIZE 213 +# endif +# define DEGRADE_RATE 50 + +word GC_min_sp; /* Coolest stack pointer value from which we've */ + /* already cleared the stack. */ + +# ifdef STACK_GROWS_DOWN +# define COOLER_THAN > +# define HOTTER_THAN < +# define MAKE_COOLER(x,y) if ((word)(x)+(y) > (word)(x)) {(x) += (y);} \ + else {(x) = (word)ONES;} +# define MAKE_HOTTER(x,y) (x) -= (y) +# else +# define COOLER_THAN < +# define HOTTER_THAN > +# define MAKE_COOLER(x,y) if ((word)(x)-(y) < (word)(x)) {(x) -= (y);} else {(x) = 0;} +# define MAKE_HOTTER(x,y) (x) += (y) +# endif + +word GC_high_water; + /* "hottest" stack pointer value we have seen */ + /* recently. Degrades over time. */ + +word GC_words_allocd_at_reset; + +#if defined(ASM_CLEAR_CODE) && !defined(THREADS) + extern ptr_t GC_clear_stack_inner(); +#endif + +#if !defined(ASM_CLEAR_CODE) && !defined(THREADS) +/* Clear the stack up to about limit. Return arg. */ +/*ARGSUSED*/ +ptr_t GC_clear_stack_inner(arg, limit) +ptr_t arg; +word limit; +{ + word dummy[CLEAR_SIZE]; + + BZERO(dummy, CLEAR_SIZE*sizeof(word)); + if ((word)(dummy) COOLER_THAN limit) { + (void) GC_clear_stack_inner(arg, limit); + } + /* Make sure the recursive call is not a tail call, and the bzero */ + /* call is not recognized as dead code. */ + GC_noop1((word)dummy); + return(arg); +} +#endif + +/* Clear some of the inaccessible part of the stack. Returns its */ +/* argument, so it can be used in a tail call position, hence clearing */ +/* another frame. */ +ptr_t GC_clear_stack(arg) +ptr_t arg; +{ + register word sp = (word)GC_approx_sp(); /* Hotter than actual sp */ +# ifdef THREADS + word dummy[CLEAR_SIZE]; +# else + register word limit; +# endif + +# define SLOP 400 + /* Extra bytes we clear every time. This clears our own */ + /* activation record, and should cause more frequent */ + /* clearing near the cold end of the stack, a good thing. */ +# define GC_SLOP 4000 + /* We make GC_high_water this much hotter than we really saw */ + /* saw it, to cover for GC noise etc. above our current frame. */ +# define CLEAR_THRESHOLD 100000 + /* We restart the clearing process after this many bytes of */ + /* allocation. Otherwise very heavily recursive programs */ + /* with sparse stacks may result in heaps that grow almost */ + /* without bounds. As the heap gets larger, collection */ + /* frequency decreases, thus clearing frequency would decrease, */ + /* thus more junk remains accessible, thus the heap gets */ + /* larger ... */ +# ifdef THREADS + BZERO(dummy, CLEAR_SIZE*sizeof(word)); +# else + if (GC_gc_no > GC_stack_last_cleared) { + /* Start things over, so we clear the entire stack again */ + if (GC_stack_last_cleared == 0) GC_high_water = (word) GC_stackbottom; + GC_min_sp = GC_high_water; + GC_stack_last_cleared = GC_gc_no; + GC_words_allocd_at_reset = GC_words_allocd; + } + /* Adjust GC_high_water */ + MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); + if (sp HOTTER_THAN GC_high_water) { + GC_high_water = sp; + } + MAKE_HOTTER(GC_high_water, GC_SLOP); + limit = GC_min_sp; + MAKE_HOTTER(limit, SLOP); + if (sp COOLER_THAN limit) { + limit &= ~0xf; /* Make it sufficiently aligned for assembly */ + /* implementations of GC_clear_stack_inner. */ + GC_min_sp = sp; + return(GC_clear_stack_inner(arg, limit)); + } else if (WORDS_TO_BYTES(GC_words_allocd - GC_words_allocd_at_reset) + > CLEAR_THRESHOLD) { + /* Restart clearing process, but limit how much clearing we do. */ + GC_min_sp = sp; + MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4); + if (GC_min_sp HOTTER_THAN GC_high_water) GC_min_sp = GC_high_water; + GC_words_allocd_at_reset = GC_words_allocd; + } +# endif + return(arg); +} + + +/* Return a pointer to the base address of p, given a pointer to a */ +/* an address within an object. Return 0 o.w. */ +# ifdef __STDC__ + GC_PTR GC_base(GC_PTR p) +# else + GC_PTR GC_base(p) + GC_PTR p; +# endif +{ + register word r; + register struct hblk *h; + register bottom_index *bi; + register hdr *candidate_hdr; + register word limit; + + r = (word)p; + if (!GC_is_initialized) return 0; + h = HBLKPTR(r); + GET_BI(r, bi); + candidate_hdr = HDR_FROM_BI(bi, r); + if (candidate_hdr == 0) return(0); + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) { + h = FORWARDED_ADDR(h,candidate_hdr); + r = (word)h + HDR_BYTES; + candidate_hdr = HDR(h); + } + if (candidate_hdr -> hb_map == GC_invalid_map) return(0); + /* Make sure r points to the beginning of the object */ + r &= ~(WORDS_TO_BYTES(1) - 1); + { + register int offset = (char *)r - (char *)(HBLKPTR(r)); + register signed_word sz = candidate_hdr -> hb_sz; + +# ifdef ALL_INTERIOR_POINTERS + register map_entry_type map_entry; + + map_entry = MAP_ENTRY((candidate_hdr -> hb_map), offset); + if (map_entry == OBJ_INVALID) { + return(0); + } + r -= WORDS_TO_BYTES(map_entry); + limit = r + WORDS_TO_BYTES(sz); +# else + register int correction; + + offset = BYTES_TO_WORDS(offset - HDR_BYTES); + correction = offset % sz; + r -= (WORDS_TO_BYTES(correction)); + limit = r + WORDS_TO_BYTES(sz); + if (limit > (word)(h + 1) + && sz <= BYTES_TO_WORDS(HBLKSIZE) - HDR_WORDS) { + return(0); + } +# endif + if ((word)p >= limit) return(0); + } + return((GC_PTR)r); +} + + +/* Return the size of an object, given a pointer to its base. */ +/* (For small obects this also happens to work from interior pointers, */ +/* but that shouldn't be relied upon.) */ +# ifdef __STDC__ + size_t GC_size(GC_PTR p) +# else + size_t GC_size(p) + GC_PTR p; +# endif +{ + register int sz; + register hdr * hhdr = HDR(p); + + sz = WORDS_TO_BYTES(hhdr -> hb_sz); + if (sz < 0) { + return(-sz); + } else { + return(sz); + } +} + +size_t GC_get_heap_size GC_PROTO(()) +{ + return ((size_t) GC_heapsize); +} + +size_t GC_get_bytes_since_gc GC_PROTO(()) +{ + return ((size_t) WORDS_TO_BYTES(GC_words_allocd)); +} + +GC_bool GC_is_initialized = FALSE; + +void GC_init() +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + GC_init_inner(); + UNLOCK(); + ENABLE_SIGNALS(); + +} + +#ifdef MSWIN32 + extern void GC_init_win32(); +#endif + +extern void GC_setpagesize(); + +void GC_init_inner() +{ +# ifndef THREADS + word dummy; +# endif + + if (GC_is_initialized) return; + GC_setpagesize(); + GC_exclude_static_roots(beginGC_arrays, end_gc_area); +# ifdef PRINTSTATS + if ((ptr_t)endGC_arrays != (ptr_t)(&GC_obj_kinds)) { + GC_printf0("Reordering linker, didn't exclude obj_kinds\n"); + } +# endif +# ifdef MSWIN32 + GC_init_win32(); +# endif +# if defined(LINUX) && (defined(POWERPC) || defined(ALPHA) || \ + defined(SPARC) || defined(MIPS)) + GC_init_linux_data_start(); +# endif +# ifdef SOLARIS_THREADS + GC_thr_init(); + /* We need dirty bits in order to find live stack sections. */ + GC_dirty_init(); +# endif +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_JDK_THREADS) + GC_thr_init(); +# endif +# if !defined(THREADS) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) + if (GC_stackbottom == 0) { + GC_stackbottom = GC_get_stack_base(); + } +# endif + if (sizeof (ptr_t) != sizeof(word)) { + ABORT("sizeof (ptr_t) != sizeof(word)\n"); + } + if (sizeof (signed_word) != sizeof(word)) { + ABORT("sizeof (signed_word) != sizeof(word)\n"); + } + if (sizeof (struct hblk) != HBLKSIZE) { + ABORT("sizeof (struct hblk) != HBLKSIZE\n"); + } +# ifndef THREADS +# if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) + ABORT( + "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd\n"); +# endif +# if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) + ABORT( + "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd\n"); +# endif +# ifdef STACK_GROWS_DOWN + if ((word)(&dummy) > (word)GC_stackbottom) { + GC_err_printf0( + "STACK_GROWS_DOWN is defd, but stack appears to grow up\n"); +# ifndef UTS4 /* Compiler bug workaround */ + GC_err_printf2("sp = 0x%lx, GC_stackbottom = 0x%lx\n", + (unsigned long) (&dummy), + (unsigned long) GC_stackbottom); +# endif + ABORT("stack direction 3\n"); + } +# else + if ((word)(&dummy) < (word)GC_stackbottom) { + GC_err_printf0( + "STACK_GROWS_UP is defd, but stack appears to grow down\n"); + GC_err_printf2("sp = 0x%lx, GC_stackbottom = 0x%lx\n", + (unsigned long) (&dummy), + (unsigned long) GC_stackbottom); + ABORT("stack direction 4"); + } +# endif +# endif +# if !defined(_AUX_SOURCE) || defined(__GNUC__) + if ((word)(-1) < (word)0) { + GC_err_printf0("The type word should be an unsigned integer type\n"); + GC_err_printf0("It appears to be signed\n"); + ABORT("word"); + } +# endif + if ((signed_word)(-1) >= (signed_word)0) { + GC_err_printf0( + "The type signed_word should be a signed integer type\n"); + GC_err_printf0("It appears to be unsigned\n"); + ABORT("signed_word"); + } + + /* Add initial guess of root sets. Do this first, since sbrk(0) */ + /* might be used. */ + GC_register_data_segments(); + GC_init_headers(); + GC_bl_init(); + GC_mark_init(); + if (!GC_expand_hp_inner((word)MINHINCR)) { + GC_err_printf0("Can't start up: not enough memory\n"); + EXIT(); + } + /* Preallocate large object map. It's otherwise inconvenient to */ + /* deal with failure. */ + if (!GC_add_map_entry((word)0)) { + GC_err_printf0("Can't start up: not enough memory\n"); + EXIT(); + } + GC_register_displacement_inner(0L); +# ifdef MERGE_SIZES + GC_init_size_map(); +# endif +# ifdef PCR + if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay) { + ABORT("Can't lock load state\n"); + } else if (PCR_IL_Unlock() != PCR_ERes_okay) { + ABORT("Can't unlock load state\n"); + } + PCR_IL_Unlock(); + GC_pcr_install(); +# endif + /* Get black list set up */ + GC_gcollect_inner(); +# ifdef STUBBORN_ALLOC + GC_stubborn_init(); +# endif + GC_is_initialized = TRUE; + /* Convince lint that some things are used */ +# ifdef LINT + { + extern char * GC_copyright[]; + extern int GC_read(); + extern void GC_register_finalizer_no_order(); + + GC_noop(GC_copyright, GC_find_header, + GC_push_one, GC_call_with_alloc_lock, GC_read, + GC_dont_expand, +# ifndef NO_DEBUGGING + GC_dump, +# endif + GC_register_finalizer_no_order); + } +# endif +} + +void GC_enable_incremental GC_PROTO(()) +{ +# if !defined(SMALL_CONFIG) + if (!GC_find_leak) { + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + if (GC_incremental) goto out; + GC_setpagesize(); +# ifdef MSWIN32 + { + extern GC_bool GC_is_win32s(); + + /* VirtualProtect is not functional under win32s. */ + if (GC_is_win32s()) goto out; + } +# endif /* MSWIN32 */ +# ifndef SOLARIS_THREADS + GC_dirty_init(); +# endif + if (!GC_is_initialized) { + GC_init_inner(); + } + if (GC_dont_gc) { + /* Can't easily do it. */ + UNLOCK(); + ENABLE_SIGNALS(); + return; + } + if (GC_words_allocd > 0) { + /* There may be unmarked reachable objects */ + GC_gcollect_inner(); + } /* else we're OK in assuming everything's */ + /* clean since nothing can point to an */ + /* unmarked object. */ + GC_read_dirty(); + GC_incremental = TRUE; +out: + UNLOCK(); + ENABLE_SIGNALS(); + } +# endif +} + + +#ifdef MSWIN32 +# define LOG_FILE "gc.log" + + HANDLE GC_stdout = 0, GC_stderr; + int GC_tmp; + DWORD GC_junk; + + void GC_set_files() + { + if (!GC_stdout) { + GC_stdout = CreateFile(LOG_FILE, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, + NULL); + if (INVALID_HANDLE_VALUE == GC_stdout) ABORT("Open of log file failed"); + } + if (GC_stderr == 0) { + GC_stderr = GC_stdout; + } + } + +#endif + +#if defined(OS2) || defined(MACOS) +FILE * GC_stdout = NULL; +FILE * GC_stderr = NULL; +int GC_tmp; /* Should really be local ... */ + + void GC_set_files() + { + if (GC_stdout == NULL) { + GC_stdout = stdout; + } + if (GC_stderr == NULL) { + GC_stderr = stderr; + } + } +#endif + +#if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) + int GC_stdout = 1; + int GC_stderr = 2; +# if !defined(AMIGA) +# include <unistd.h> +# endif +#endif + +#if !defined(MSWIN32) && !defined(OS2) && !defined(MACOS) +int GC_write(fd, buf, len) +int fd; +char *buf; +size_t len; +{ + register int bytes_written = 0; + register int result; + + while (bytes_written < len) { +# ifdef SOLARIS_THREADS + result = syscall(SYS_write, fd, buf + bytes_written, + len - bytes_written); +# else + result = write(fd, buf + bytes_written, len - bytes_written); +# endif + if (-1 == result) return(result); + bytes_written += result; + } + return(bytes_written); +} +#endif /* UN*X */ + +#ifdef MSWIN32 +# define WRITE(f, buf, len) (GC_set_files(), \ + GC_tmp = WriteFile((f), (buf), \ + (len), &GC_junk, NULL),\ + (GC_tmp? 1 : -1)) +#else +# if defined(OS2) || defined(MACOS) +# define WRITE(f, buf, len) (GC_set_files(), \ + GC_tmp = fwrite((buf), 1, (len), (f)), \ + fflush(f), GC_tmp) +# else +# define WRITE(f, buf, len) GC_write((f), (buf), (len)) +# endif +#endif + +/* A version of printf that is unlikely to call malloc, and is thus safer */ +/* to call from the collector in case malloc has been bound to GC_malloc. */ +/* Assumes that no more than 1023 characters are written at once. */ +/* Assumes that all arguments have been converted to something of the */ +/* same size as long, and that the format conversions expect something */ +/* of that size. */ +void GC_printf(format, a, b, c, d, e, f) +char * format; +long a, b, c, d, e, f; +{ + char buf[1025]; + + if (GC_quiet) return; + buf[1024] = 0x15; + (void) sprintf(buf, format, a, b, c, d, e, f); + if (buf[1024] != 0x15) ABORT("GC_printf clobbered stack"); + if (WRITE(GC_stdout, buf, strlen(buf)) < 0) ABORT("write to stdout failed"); +} + +void GC_err_printf(format, a, b, c, d, e, f) +char * format; +long a, b, c, d, e, f; +{ + char buf[1025]; + + buf[1024] = 0x15; + (void) sprintf(buf, format, a, b, c, d, e, f); + if (buf[1024] != 0x15) ABORT("GC_err_printf clobbered stack"); + if (WRITE(GC_stderr, buf, strlen(buf)) < 0) ABORT("write to stderr failed"); +} + +void GC_err_puts(s) +char *s; +{ + if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed"); +} + +# if defined(__STDC__) || defined(__cplusplus) + void GC_default_warn_proc(char *msg, GC_word arg) +# else + void GC_default_warn_proc(msg, arg) + char *msg; + GC_word arg; +# endif +{ + GC_err_printf1(msg, (unsigned long)arg); +} + +GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; + +# if defined(__STDC__) || defined(__cplusplus) + GC_warn_proc GC_set_warn_proc(GC_warn_proc p) +# else + GC_warn_proc GC_set_warn_proc(p) + GC_warn_proc p; +# endif +{ + GC_warn_proc result; + + LOCK(); + result = GC_current_warn_proc; + GC_current_warn_proc = p; + UNLOCK(); + return(result); +} + + +#ifndef PCR +void GC_abort(msg) +char * msg; +{ + GC_err_printf1("%s\n", msg); + (void) abort(); +} +#endif + +#ifdef NEED_CALLINFO + +void GC_print_callers (info) +struct callinfo info[NFRAMES]; +{ + register int i; + +# if NFRAMES == 1 + GC_err_printf0("\tCaller at allocation:\n"); +# else + GC_err_printf0("\tCall chain at allocation:\n"); +# endif + for (i = 0; i < NFRAMES; i++) { + if (info[i].ci_pc == 0) break; +# if NARGS > 0 + { + int j; + + GC_err_printf0("\t\targs: "); + for (j = 0; j < NARGS; j++) { + if (j != 0) GC_err_printf0(", "); + GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), + ~(info[i].ci_arg[j])); + } + GC_err_printf0("\n"); + } +# endif + GC_err_printf1("\t\t##PC##= 0x%X\n", info[i].ci_pc); + } +} + +#endif /* SAVE_CALL_CHAIN */ + +# ifdef SRC_M3 +void GC_enable() +{ + GC_dont_gc--; +} + +void GC_disable() +{ + GC_dont_gc++; +} +# endif + +#if !defined(NO_DEBUGGING) + +void GC_dump() +{ + GC_printf0("***Static roots:\n"); + GC_print_static_roots(); + GC_printf0("\n***Heap sections:\n"); + GC_print_heap_sects(); + GC_printf0("\n***Free blocks:\n"); + GC_print_hblkfreelist(); + GC_printf0("\n***Blocks in use:\n"); + GC_print_block_list(); +} + +# endif /* NO_DEBUGGING */ diff --git a/gc/new_hblk.c b/gc/new_hblk.c new file mode 100644 index 0000000..9f32ae0 --- /dev/null +++ b/gc/new_hblk.c @@ -0,0 +1,244 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + * This file contains the functions: + * ptr_t GC_build_flXXX(h, old_fl) + * void GC_new_hblk(n) + */ +/* Boehm, May 19, 1994 2:09 pm PDT */ + + +# include <stdio.h> +# include "gc_priv.h" + +#ifndef SMALL_CONFIG +/* + * Build a free list for size 1 objects inside hblk h. Set the last link to + * be ofl. Return a pointer tpo the first free list entry. + */ +ptr_t GC_build_fl1(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = (word)(p); + p[2] = (word)(p+1); + p[3] = (word)(p+2); + p += 4; + for (; p < lim; p += 4) { + p[0] = (word)(p-1); + p[1] = (word)(p); + p[2] = (word)(p+1); + p[3] = (word)(p+2); + }; + return((ptr_t)(p-1)); +} + +/* The same for size 2 cleared objects */ +ptr_t GC_build_fl_clear2(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + p += 4; + for (; p < lim; p += 4) { + p[0] = (word)(p-2); + p[1] = 0; + p[2] = (word)p; + p[3] = 0; + }; + return((ptr_t)(p-2)); +} + +/* The same for size 3 cleared objects */ +ptr_t GC_build_fl_clear3(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1) - 2; + + p[0] = (word)ofl; + p[1] = 0; + p[2] = 0; + p += 3; + for (; p < lim; p += 3) { + p[0] = (word)(p-3); + p[1] = 0; + p[2] = 0; + }; + return((ptr_t)(p-3)); +} + +/* The same for size 4 cleared objects */ +ptr_t GC_build_fl_clear4(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[1] = 0; + p[2] = 0; + p[3] = 0; + p += 4; + for (; p < lim; p += 4) { + p[0] = (word)(p-4); + p[1] = 0; + p[2] = 0; + p[3] = 0; + }; + return((ptr_t)(p-4)); +} + +/* The same for size 2 uncleared objects */ +ptr_t GC_build_fl2(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[2] = (word)p; + p += 4; + for (; p < lim; p += 4) { + p[0] = (word)(p-2); + p[2] = (word)p; + }; + return((ptr_t)(p-2)); +} + +/* The same for size 4 uncleared objects */ +ptr_t GC_build_fl4(h, ofl) +struct hblk *h; +ptr_t ofl; +{ + register word * p = (word *)h; + register word * lim = (word *)(h + 1); + + p[0] = (word)ofl; + p[4] = (word)p; + p += 8; + for (; p < lim; p += 8) { + p[0] = (word)(p-4); + p[4] = (word)p; + }; + return((ptr_t)(p-4)); +} + +#endif /* !SMALL_CONFIG */ + +/* + * Allocate a new heapblock for small objects of size n. + * Add all of the heapblock's objects to the free list for objects + * of that size. + * Set all mark bits if objects are uncollectable. + * Will fail to do anything if we are out of memory. + */ +void GC_new_hblk(sz, kind) +register word sz; +int kind; +{ + register word *p, + *prev; + word *last_object; /* points to last object in new hblk */ + register struct hblk *h; /* the new heap block */ + register GC_bool clear = GC_obj_kinds[kind].ok_init; + +# ifdef PRINTSTATS + if ((sizeof (struct hblk)) > HBLKSIZE) { + ABORT("HBLK SZ inconsistency"); + } +# endif + + /* Allocate a new heap block */ + h = GC_allochblk(sz, kind, 0); + if (h == 0) return; + + /* Mark all objects if appropriate. */ + if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(HDR(h)); + + /* Handle small objects sizes more efficiently. For larger objects */ + /* the difference is less significant. */ +# ifndef SMALL_CONFIG + switch (sz) { + case 1: GC_obj_kinds[kind].ok_freelist[1] = + GC_build_fl1(h, GC_obj_kinds[kind].ok_freelist[1]); + return; + case 2: if (clear) { + GC_obj_kinds[kind].ok_freelist[2] = + GC_build_fl_clear2(h, GC_obj_kinds[kind].ok_freelist[2]); + } else { + GC_obj_kinds[kind].ok_freelist[2] = + GC_build_fl2(h, GC_obj_kinds[kind].ok_freelist[2]); + } + return; + case 3: if (clear) { + GC_obj_kinds[kind].ok_freelist[3] = + GC_build_fl_clear3(h, GC_obj_kinds[kind].ok_freelist[3]); + return; + } else { + /* It's messy to do better than the default here. */ + break; + } + case 4: if (clear) { + GC_obj_kinds[kind].ok_freelist[4] = + GC_build_fl_clear4(h, GC_obj_kinds[kind].ok_freelist[4]); + } else { + GC_obj_kinds[kind].ok_freelist[4] = + GC_build_fl4(h, GC_obj_kinds[kind].ok_freelist[4]); + } + return; + default: + break; + } +# endif /* !SMALL_CONFIG */ + + /* Clear the page if necessary. */ + if (clear) BZERO(h, HBLKSIZE); + + /* Add objects to free list */ + p = &(h -> hb_body[sz]); /* second object in *h */ + prev = &(h -> hb_body[0]); /* One object behind p */ + last_object = (word *)((char *)h + HBLKSIZE); + last_object -= sz; + /* Last place for last object to start */ + + /* make a list of all objects in *h with head as last object */ + while (p <= last_object) { + /* current object's link points to last object */ + obj_link(p) = (ptr_t)prev; + prev = p; + p += sz; + } + p -= sz; /* p now points to last object */ + + /* + * put p (which is now head of list of objects in *h) as first + * pointer in the appropriate free list for this size. + */ + obj_link(h -> hb_body) = GC_obj_kinds[kind].ok_freelist[sz]; + GC_obj_kinds[kind].ok_freelist[sz] = ((ptr_t)p); +} + diff --git a/gc/nursery.c b/gc/nursery.c new file mode 100644 index 0000000..8bbb101 --- /dev/null +++ b/gc/nursery.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 1999 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + + +??? This implementation is incomplete. If you are trying to +??? compile this you are doing something wrong. + +#include "nursery.h" + +struct copy_obj { + ptr_t forward; /* Forwarding link for copied objects. */ + GC_copy_descriptor descr; /* Object descriptor */ + word data[1]; +} + +ptr_t GC_nursery_start; /* Start of nursery area. */ + /* Must be NURSERY_BLOCK_SIZE */ + /* aligned. */ +ptr_t GC_nursery_end; /* End of nursery area. */ +unsigned char * GC_nursery_map; + /* GC_nursery_map[i] != 0 if an object */ + /* starts on the ith 64-bit "word" of */ + /* nursery. This simple structure has */ + /* the advantage that */ + /* allocation is cheap. Lookup is */ + /* cheap for pointers to the head of */ + /* an object, which should be the */ + /* usual case. */ +# define NURSERY_MAP_NOT_START 0 /* Not start of object. */ +# define NURSERY_MAP_START 1 /* Start of object. */ +# define NURSERY_MAP_PINNED 2 /* Start of pinned obj. */ + +# ifdef ALIGN_DOUBLE +# define NURSERY_WORD_SIZE (2 * sizeof(word)) +# else +# define NURSERY_WORD_SIZE sizeof(word) +# endif + +# define NURSERY_BLOCK_SIZE (HBLKSIZE/2) + /* HBLKSIZE must be a multiple of NURSERY_BLOCK_SIZE */ +# define NURSERY_SIZE (1024 * NURSERY_BLOCK_SIZE) + +size_t GC_nursery_size = NURSERY_SIZE; + /* Must be multiple of NURSERY_BLOCK_SIZE */ + +size_t GC_nursery_blocks; /* Number of blocks in the nursery. */ + +unsigned GC_next_nursery_block; /* index of next block we will attempt */ + /* allocate from during this cycle. */ + /* If it is pinned, we won't actually */ + /* use it. */ + +unsigned short *GC_pinned; /* Number of pinned objects in ith */ + /* nursery block. */ + /* GC_pinned[i] != 0 if the ith nursery */ + /* block is pinned, and thus not used */ + /* for allocation. */ + +GC_copy_alloc_state global_alloc_state = (ptr_t)(-1); /* will overflow. */ + +/* Should be called with allocator lock held. */ +GC_nursery_init() { + GC_nursery_start = GET_MEM(GC_nursery_size); + GC_nursery_end = GC_nursery_start + GC_nursery_size; + GC_next_nursery_block = 0; + if (GC_nursery_start < GC_least_plausible_heap_addr) { + GC_least_plausible_heap_addr = GC_nursery_start; + } + if (GC_nursery_end > GC_greatest_plausible_heap_addr) { + GC_greatest_plausible_heap_addr = GC_nursery_end; + } + if (GC_nursery_start & (NURSERY_BLOCK_SIZE-1)) { + GC_err_printf1("Nursery area is misaligned!!"); + /* This should be impossible, since GET_MEM returns HBLKSIZE */ + /* aligned chunks, and that should be a multiple of */ + /* NURSERY_BLOCK_SIZE */ + ABORT("misaligned nursery"); + } + GC_nursery_map = GET_MEM(GC_nursery_size/NURSERY_WORD_SIZE); + /* Map is cleared a block at a time when we allocate from the block. */ + /* BZERO(GC_nursery_map, GC_nursery_size/NURSERY_WORD_SIZE); */ + GC_nursery_blocks = GC_nursery_size/NURSERY_BLOCK_SIZE; + GC_pinned = GC_scratch_alloc(GC_nursery_blocks * sizeof(unsigned short)); + BZERO(GC_pinned, GC_nursery_blocks); +} + +/* Pin all nursery objects referenced from mark stack. */ +void GC_pin_mark_stack_objects(void) { + for each possible pointer current in a mark stack object + if (current >= GC_nursery_start && current < GC_nursery_end) { + unsigned offset = current - GC_nursery_start; + unsigned word_offset = BYTES_TO_WORDS(offset); + unsigned blockno = (current - GC_nursery_start)/NURSERY_BLOCK_SIZE; + while (GC_nursery_map[word_offset] == NURSERY_MAP_NOT_START) { + --word_offset; + } + if (GC_nursery_map[word_offset] != NURSERY_MAP_PINNED) { + GC_nursery_map[word_offset] = NURSERY_MAP_PINNED; + ++GC_pinned[blockno]; + ??Push object at GC_nursery_start + WORDS_TO_BYTES(word_offset) + ??onto stack. + } + } + } +} + +/* Caller holds allocation lock. */ +void GC_collect_nursery(void) { + int i; + ptr_t scan_ptr = 0; + ?? old_mark_stack_top; + STOP_WORLD; + for (i = 0; i < GC_nursery_blocks; ++i) GC_pinned[i] = 0; + GC_push_all_roots(); + old_mark_stack_top = GC_mark_stack_top(); + GC_pin_mark_stack_objects(); + START_WORLD; +} + +/* Initialize an allocation state so that it can be used for */ +/* allocation. This implicitly reserves a small section of the */ +/* nursery for use with his allocator. */ +void GC_init_copy_alloc_state(GC_copy_alloc_state *) + unsigned next_block; + ptr_t block_addr; + LOCK(); + next_block = GC_next_nursery_block; + while(is_pinned[next_block] && next_block < GC_nursery_blocks) { + ++next_block; + } + if (next_block < GC_nursery_blocks) { + block_addr = GC_nursery_start + NURSERY_BLOCK_SIZE * next_block; + GC_next_nursery_block = next_block + 1; + BZERO(GC_nursery_map + next_block * + (NURSERY_BLOCK_SIZE/NURSERY_WORD_SIZE), + NURSERY_BLOCK_SIZE/NURSERY_WORD_SIZE); + *GC_copy_alloc_state = block_addr; + UNLOCK(); + } else { + GC_collect_nursery(); + GC_next_nursery_block = 0; + UNLOCK(); + get_new_block(s); + } +} + +GC_PTR GC_copying_malloc2(GC_copy_descriptor *d, GC_copy_alloc_state *s) { + size_t sz = GC_SIZE_FROM_DESCRIPTOR(d); + ptrdiff_t offset; + ptr_t result = *s; + ptr_t new = result + sz; + if (new & COPY_BLOCK_MASK <= result & COPY_BLOCK_MASK> { + GC_init_copy_alloc_state(s); + result = *s; + new = result + sz; + GC_ASSERT(new & COPY_BLOCK_MASK > result & COPY_BLOCK_MASK> + } + (struct copy_obj *)result -> descr = d; + (struct copy_obj *)result -> forward = 0; + offset = (result - GC_nursery_start)/NURSERY_WORD_SIZE; + GC_nursery_map[offset] = NURSERY_MAP_NOT_START; +} + +GC_PTR GC_copying_malloc(GC_copy_descriptor *d) { +} diff --git a/gc/nursery.h b/gc/nursery.h new file mode 100755 index 0000000..d109ff0 --- /dev/null +++ b/gc/nursery.h @@ -0,0 +1,90 @@ + +/* + * Copyright (c) 1999 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +/* + * THIS IMPLEMENTATION FOR THIS INTERFACE IS INCOMPLETE. + * NONE OF THIS HAS BEEN TESTED. DO NOT USE. + * + * Comments on the interface are appreciated, especially from + * potential users of the interface. + * + * This is a Bartlett style copying collector for young objects. + * We assume for now that all objects allocated through this + * mechanism have pointers only in the first BITMAP_BITS words. + * (On a 32-bit machine, BITMAP_BITS is 30.) + * Objects allocated in this manner should be rarely referenced + * by objects not allocated either through this interface, or through + * the typed allocation interface. + * If this interface is used, we assume that type information provided + * through either this or the typed allocation interface is valid + * in a stronger sense: + * + * 1) No pointers are stored in fields not marked as such. + * (Otherwise it is only necessary that objects referenced by + * fields marked as nonpointers are also reachable via another + * path.) + * 2) Values stored in pointer fields are either not addresses in + * the heap, or they really are pointers. In the latter case, it + * is acceptable to move the object they refer to, and to update + * the pointer. + * + * GC_free may not be invoked on objects allocated with GC_copying_malloc. + * + * No extra space is added to the end of objects allocated through this + * interface. If the client needs to maintain pointers past the + * end, the size should be explicitly padded. + * + * We assume that calls to this will usually be compiler generated. + * Hence the interface is allowed to be a bit ugly in return for speed. + */ + +#include "gc_copy_descr.h" + +/* GC_copy_descr.h must define */ +/* GC_SIZE_FROM_DESCRIPTOR(descr) and */ +/* GC_BIT_MAP_FROM_DESCRIPTOR(descr). */ +/* It may either be the GC supplied version of the header file, or a */ +/* client specific one that derives the information from a client- */ +/* specific type descriptor. */ + +typedef GC_PTR GC_copy_alloc_state; + /* Current allocator state. */ + /* Multiple allocation states */ + /* may be used for concurrent */ + /* allocation, or to enhance */ + /* locality. */ + /* Should be treated as opaque. */ + +/* Allocate a memory block of size given in the descriptor, and with */ +/* pointer layout given by the descriptor. The resulting block may not */ +/* be cleared, and should immediately be initialized by the client. */ +/* (A concurrent GC may see an uninitialized pointer field. If it */ +/* points outside the nursery, that's fine. If it points inside, it */ +/* may retain an object, and be relocated. But that's also fine, since */ +/* the new value will be immediately overwritten. */ +/* This variant acquires the allocation lock, and uses a default */ +/* global allocation state. */ +GC_PTR GC_copying_malloc(GC_copy_descriptor); + +/* A variant of the above that does no locking on the fast path, */ +/* and passes an explicit pointer to an allocation state. */ +/* The allocation state is updated. */ +/* There will eventually need to be a macro or inline function version */ +/* of this. */ +GC_PTR GC_copying_malloc2(GC_copy_descriptor, GC_copy_alloc_state *); + +/* Initialize an allocation state so that it can be used for */ +/* allocation. This implicitly reserves a small section of the */ +/* nursery for use with this allocator. */ +void GC_init_copy_alloc_state(GC_copy_alloc_state *); diff --git a/gc/obj_map.c b/gc/obj_map.c new file mode 100644 index 0000000..82ebf31 --- /dev/null +++ b/gc/obj_map.c @@ -0,0 +1,142 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991, 1992 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, October 9, 1995 1:09 pm PDT */ + +/* Routines for maintaining maps describing heap block + * layouts for various object sizes. Allows fast pointer validity checks + * and fast location of object start locations on machines (such as SPARC) + * with slow division. + */ + +# include "gc_priv.h" + +char * GC_invalid_map = 0; + +/* Invalidate the object map associated with a block. Free blocks */ +/* are identified by invalid maps. */ +void GC_invalidate_map(hhdr) +hdr *hhdr; +{ + register int displ; + + if (GC_invalid_map == 0) { + GC_invalid_map = GC_scratch_alloc(MAP_SIZE); + if (GC_invalid_map == 0) { + GC_err_printf0( + "Cant initialize GC_invalid_map: insufficient memory\n"); + EXIT(); + } + for (displ = 0; displ < HBLKSIZE; displ++) { + MAP_ENTRY(GC_invalid_map, displ) = OBJ_INVALID; + } + } + hhdr -> hb_map = GC_invalid_map; +} + +/* Consider pointers that are offset bytes displaced from the beginning */ +/* of an object to be valid. */ + +# if defined(__STDC__) || defined(__cplusplus) + void GC_register_displacement(GC_word offset) +# else + void GC_register_displacement(offset) + GC_word offset; +# endif +{ +# ifndef ALL_INTERIOR_POINTERS + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + GC_register_displacement_inner(offset); + UNLOCK(); + ENABLE_SIGNALS(); +# endif +} + +void GC_register_displacement_inner(offset) +word offset; +{ +# ifndef ALL_INTERIOR_POINTERS + register unsigned i; + + if (offset > MAX_OFFSET) { + ABORT("Bad argument to GC_register_displacement"); + } + if (!GC_valid_offsets[offset]) { + GC_valid_offsets[offset] = TRUE; + GC_modws_valid_offsets[offset % sizeof(word)] = TRUE; + for (i = 0; i <= MAXOBJSZ; i++) { + if (GC_obj_map[i] != 0) { + if (i == 0) { + GC_obj_map[i][offset + HDR_BYTES] = (char)BYTES_TO_WORDS(offset); + } else { + register unsigned j; + register unsigned lb = WORDS_TO_BYTES(i); + + if (offset < lb) { + for (j = offset + HDR_BYTES; j < HBLKSIZE; j += lb) { + GC_obj_map[i][j] = (char)BYTES_TO_WORDS(offset); + } + } + } + } + } + } +# endif +} + + +/* Add a heap block map for objects of size sz to obj_map. */ +/* Return FALSE on failure. */ +GC_bool GC_add_map_entry(sz) +word sz; +{ + register unsigned obj_start; + register unsigned displ; + register char * new_map; + + if (sz > MAXOBJSZ) sz = 0; + if (GC_obj_map[sz] != 0) { + return(TRUE); + } + new_map = GC_scratch_alloc(MAP_SIZE); + if (new_map == 0) return(FALSE); +# ifdef PRINTSTATS + GC_printf1("Adding block map for size %lu\n", (unsigned long)sz); +# endif + for (displ = 0; displ < HBLKSIZE; displ++) { + MAP_ENTRY(new_map,displ) = OBJ_INVALID; + } + if (sz == 0) { + for(displ = 0; displ <= MAX_OFFSET; displ++) { + if (OFFSET_VALID(displ)) { + MAP_ENTRY(new_map,displ+HDR_BYTES) = BYTES_TO_WORDS(displ); + } + } + } else { + for (obj_start = HDR_BYTES; + obj_start + WORDS_TO_BYTES(sz) <= HBLKSIZE; + obj_start += WORDS_TO_BYTES(sz)) { + for (displ = 0; displ < WORDS_TO_BYTES(sz); displ++) { + if (OFFSET_VALID(displ)) { + MAP_ENTRY(new_map, obj_start + displ) = + BYTES_TO_WORDS(displ); + } + } + } + } + GC_obj_map[sz] = new_map; + return(TRUE); +} diff --git a/gc/os_dep.c b/gc/os_dep.c new file mode 100644 index 0000000..0236248 --- /dev/null +++ b/gc/os_dep.c @@ -0,0 +1,2461 @@ +/* + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996-1997 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +# include "gc_priv.h" + +# if defined(LINUX) && !defined(POWERPC) +# include <linux/version.h> +# if (LINUX_VERSION_CODE <= 0x10400) + /* Ugly hack to get struct sigcontext_struct definition. Required */ + /* for some early 1.3.X releases. Will hopefully go away soon. */ + /* in some later Linux releases, asm/sigcontext.h may have to */ + /* be included instead. */ +# define __KERNEL__ +# include <asm/signal.h> +# undef __KERNEL__ +# else + /* Kernels prior to 2.1.1 defined struct sigcontext_struct instead of */ + /* struct sigcontext. libc6 (glibc2) uses "struct sigcontext" in */ + /* prototypes, so we have to include the top-level sigcontext.h to */ + /* make sure the former gets defined to be the latter if appropriate. */ +# include <features.h> +# if 2 <= __GLIBC__ +# if 0 == __GLIBC_MINOR__ + /* glibc 2.1 no longer has sigcontext.h. But signal.h */ + /* has the right declaration for glibc 2.1. */ +# include <sigcontext.h> +# endif /* 0 == __GLIBC_MINOR__ */ +# else /* not 2 <= __GLIBC__ */ + /* libc5 doesn't have <sigcontext.h>: go directly with the kernel */ + /* one. Check LINUX_VERSION_CODE to see which we should reference. */ +# include <asm/sigcontext.h> +# endif /* 2 <= __GLIBC__ */ +# endif +# endif +# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) && !defined(MACOS) +# include <sys/types.h> +# if !defined(MSWIN32) && !defined(SUNOS4) +# include <unistd.h> +# endif +# endif + +# include <stdio.h> +# include <signal.h> + +/* Blatantly OS dependent routines, except for those that are related */ +/* dynamic loading. */ + +# if !defined(THREADS) && !defined(STACKBOTTOM) && defined(HEURISTIC2) +# define NEED_FIND_LIMIT +# endif + +# if defined(IRIX_THREADS) +# define NEED_FIND_LIMIT +# endif + +# if (defined(SUNOS4) & defined(DYNAMIC_LOADING)) && !defined(PCR) +# define NEED_FIND_LIMIT +# endif + +# if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR) +# define NEED_FIND_LIMIT +# endif + +# if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || \ + defined(ALPHA) || defined(MIPS)) +# define NEED_FIND_LIMIT +# endif + +#ifdef NEED_FIND_LIMIT +# include <setjmp.h> +#endif + +#ifdef FREEBSD +# include <machine/trap.h> +#endif + +#ifdef AMIGA +# include <proto/exec.h> +# include <proto/dos.h> +# include <dos/dosextens.h> +# include <workbench/startup.h> +#endif + +#ifdef MSWIN32 +# define WIN32_LEAN_AND_MEAN +# define NOSERVICE +# include <windows.h> +#endif + +#ifdef MACOS +# include <Processes.h> +#endif + +#ifdef IRIX5 +# include <sys/uio.h> +# include <malloc.h> /* for locking */ +#endif +#ifdef USE_MMAP +# include <sys/types.h> +# include <sys/mman.h> +# include <sys/stat.h> +# include <fcntl.h> +#endif + +#ifdef SUNOS5SIGS +# include <sys/siginfo.h> +# undef setjmp +# undef longjmp +# define setjmp(env) sigsetjmp(env, 1) +# define longjmp(env, val) siglongjmp(env, val) +# define jmp_buf sigjmp_buf +#endif + +#ifdef DJGPP + /* Apparently necessary for djgpp 2.01. May casuse problems with */ + /* other versions. */ + typedef long unsigned int caddr_t; +#endif + +#ifdef PCR +# include "il/PCR_IL.h" +# include "th/PCR_ThCtl.h" +# include "mm/PCR_MM.h" +#endif + +#if !defined(NO_EXECUTE_PERMISSION) +# define OPT_PROT_EXEC PROT_EXEC +#else +# define OPT_PROT_EXEC 0 +#endif + +#if defined(LINUX) && (defined(POWERPC) || defined(SPARC) || \ + defined(ALPHA) || defined(MIPS)) + /* The I386 case can be handled without a search. The Alpha case */ + /* used to be handled differently as well, but the rules changed */ + /* for recent Linux versions. This seems to be the easiest way to */ + /* cover all versions. */ + ptr_t GC_data_start; + + extern char * GC_copyright[]; /* Any data symbol would do. */ + + void GC_init_linux_data_start() + { + extern ptr_t GC_find_limit(); + + GC_data_start = GC_find_limit((ptr_t)GC_copyright, FALSE); + } +#endif + +# ifdef OS2 + +# include <stddef.h> + +# if !defined(__IBMC__) && !defined(__WATCOMC__) /* e.g. EMX */ + +struct exe_hdr { + unsigned short magic_number; + unsigned short padding[29]; + long new_exe_offset; +}; + +#define E_MAGIC(x) (x).magic_number +#define EMAGIC 0x5A4D +#define E_LFANEW(x) (x).new_exe_offset + +struct e32_exe { + unsigned char magic_number[2]; + unsigned char byte_order; + unsigned char word_order; + unsigned long exe_format_level; + unsigned short cpu; + unsigned short os; + unsigned long padding1[13]; + unsigned long object_table_offset; + unsigned long object_count; + unsigned long padding2[31]; +}; + +#define E32_MAGIC1(x) (x).magic_number[0] +#define E32MAGIC1 'L' +#define E32_MAGIC2(x) (x).magic_number[1] +#define E32MAGIC2 'X' +#define E32_BORDER(x) (x).byte_order +#define E32LEBO 0 +#define E32_WORDER(x) (x).word_order +#define E32LEWO 0 +#define E32_CPU(x) (x).cpu +#define E32CPU286 1 +#define E32_OBJTAB(x) (x).object_table_offset +#define E32_OBJCNT(x) (x).object_count + +struct o32_obj { + unsigned long size; + unsigned long base; + unsigned long flags; + unsigned long pagemap; + unsigned long mapsize; + unsigned long reserved; +}; + +#define O32_FLAGS(x) (x).flags +#define OBJREAD 0x0001L +#define OBJWRITE 0x0002L +#define OBJINVALID 0x0080L +#define O32_SIZE(x) (x).size +#define O32_BASE(x) (x).base + +# else /* IBM's compiler */ + +/* A kludge to get around what appears to be a header file bug */ +# ifndef WORD +# define WORD unsigned short +# endif +# ifndef DWORD +# define DWORD unsigned long +# endif + +# define EXE386 1 +# include <newexe.h> +# include <exe386.h> + +# endif /* __IBMC__ */ + +# define INCL_DOSEXCEPTIONS +# define INCL_DOSPROCESS +# define INCL_DOSERRORS +# define INCL_DOSMODULEMGR +# define INCL_DOSMEMMGR +# include <os2.h> + + +/* Disable and enable signals during nontrivial allocations */ + +void GC_disable_signals(void) +{ + ULONG nest; + + DosEnterMustComplete(&nest); + if (nest != 1) ABORT("nested GC_disable_signals"); +} + +void GC_enable_signals(void) +{ + ULONG nest; + + DosExitMustComplete(&nest); + if (nest != 0) ABORT("GC_enable_signals"); +} + + +# else + +# if !defined(PCR) && !defined(AMIGA) && !defined(MSWIN32) \ + && !defined(MACOS) && !defined(DJGPP) && !defined(DOS4GW) + +# if defined(sigmask) && !defined(UTS4) + /* Use the traditional BSD interface */ +# define SIGSET_T int +# define SIG_DEL(set, signal) (set) &= ~(sigmask(signal)) +# define SIG_FILL(set) (set) = 0x7fffffff + /* Setting the leading bit appears to provoke a bug in some */ + /* longjmp implementations. Most systems appear not to have */ + /* a signal 32. */ +# define SIGSETMASK(old, new) (old) = sigsetmask(new) +# else + /* Use POSIX/SYSV interface */ +# define SIGSET_T sigset_t +# define SIG_DEL(set, signal) sigdelset(&(set), (signal)) +# define SIG_FILL(set) sigfillset(&set) +# define SIGSETMASK(old, new) sigprocmask(SIG_SETMASK, &(new), &(old)) +# endif + +static GC_bool mask_initialized = FALSE; + +static SIGSET_T new_mask; + +static SIGSET_T old_mask; + +static SIGSET_T dummy; + +#if defined(PRINTSTATS) && !defined(THREADS) +# define CHECK_SIGNALS + int GC_sig_disabled = 0; +#endif + +void GC_disable_signals() +{ + if (!mask_initialized) { + SIG_FILL(new_mask); + + SIG_DEL(new_mask, SIGSEGV); + SIG_DEL(new_mask, SIGILL); + SIG_DEL(new_mask, SIGQUIT); +# ifdef SIGBUS + SIG_DEL(new_mask, SIGBUS); +# endif +# ifdef SIGIOT + SIG_DEL(new_mask, SIGIOT); +# endif +# ifdef SIGEMT + SIG_DEL(new_mask, SIGEMT); +# endif +# ifdef SIGTRAP + SIG_DEL(new_mask, SIGTRAP); +# endif + mask_initialized = TRUE; + } +# ifdef CHECK_SIGNALS + if (GC_sig_disabled != 0) ABORT("Nested disables"); + GC_sig_disabled++; +# endif + SIGSETMASK(old_mask,new_mask); +} + +void GC_enable_signals() +{ +# ifdef CHECK_SIGNALS + if (GC_sig_disabled != 1) ABORT("Unmatched enable"); + GC_sig_disabled--; +# endif + SIGSETMASK(dummy,old_mask); +} + +# endif /* !PCR */ + +# endif /*!OS/2 */ + +/* Ivan Demakov: simplest way (to me) */ +#ifdef DOS4GW + void GC_disable_signals() { } + void GC_enable_signals() { } +#endif + +/* Find the page size */ +word GC_page_size; + +# ifdef MSWIN32 + void GC_setpagesize() + { + SYSTEM_INFO sysinfo; + + GetSystemInfo(&sysinfo); + GC_page_size = sysinfo.dwPageSize; + } + +# else +# if defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(USE_MMAP) \ + || defined(USE_MUNMAP) + void GC_setpagesize() + { + GC_page_size = GETPAGESIZE(); + } +# else + /* It's acceptable to fake it. */ + void GC_setpagesize() + { + GC_page_size = HBLKSIZE; + } +# endif +# endif + +/* + * Find the base of the stack. + * Used only in single-threaded environment. + * With threads, GC_mark_roots needs to know how to do this. + * Called with allocator lock held. + */ +# ifdef MSWIN32 +# define is_writable(prot) ((prot) == PAGE_READWRITE \ + || (prot) == PAGE_WRITECOPY \ + || (prot) == PAGE_EXECUTE_READWRITE \ + || (prot) == PAGE_EXECUTE_WRITECOPY) +/* Return the number of bytes that are writable starting at p. */ +/* The pointer p is assumed to be page aligned. */ +/* If base is not 0, *base becomes the beginning of the */ +/* allocation region containing p. */ +word GC_get_writable_length(ptr_t p, ptr_t *base) +{ + MEMORY_BASIC_INFORMATION buf; + word result; + word protect; + + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf)) ABORT("Weird VirtualQuery result"); + if (base != 0) *base = (ptr_t)(buf.AllocationBase); + protect = (buf.Protect & ~(PAGE_GUARD | PAGE_NOCACHE)); + if (!is_writable(protect)) { + return(0); + } + if (buf.State != MEM_COMMIT) return(0); + return(buf.RegionSize); +} + +ptr_t GC_get_stack_base() +{ + int dummy; + ptr_t sp = (ptr_t)(&dummy); + ptr_t trunc_sp = (ptr_t)((word)sp & ~(GC_page_size - 1)); + word size = GC_get_writable_length(trunc_sp, 0); + + return(trunc_sp + size); +} + + +# else + +# ifdef OS2 + +ptr_t GC_get_stack_base() +{ + PTIB ptib; + PPIB ppib; + + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + GC_err_printf0("DosGetInfoBlocks failed\n"); + ABORT("DosGetInfoBlocks failed\n"); + } + return((ptr_t)(ptib -> tib_pstacklimit)); +} + +# else + +# ifdef AMIGA + +ptr_t GC_get_stack_base() +{ + struct Process *proc = (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 42,567,574 */ + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS + && proc->pr_CLI != NULL) { + /* first ULONG is StackSize */ + /*longPtr = proc->pr_ReturnAddr; + size = longPtr[0];*/ + + return (char *)proc->pr_ReturnAddr + sizeof(ULONG); + } else { + return (char *)proc->pr_Task.tc_SPUpper; + } +} + +#if 0 /* old version */ +ptr_t GC_get_stack_base() +{ + extern struct WBStartup *_WBenchMsg; + extern long __base; + extern long __stack; + struct Task *task; + struct Process *proc; + struct CommandLineInterface *cli; + long size; + + if ((task = FindTask(0)) == 0) { + GC_err_puts("Cannot find own task structure\n"); + ABORT("task missing"); + } + proc = (struct Process *)task; + cli = BADDR(proc->pr_CLI); + + if (_WBenchMsg != 0 || cli == 0) { + size = (char *)task->tc_SPUpper - (char *)task->tc_SPLower; + } else { + size = cli->cli_DefaultStack * 4; + } + return (ptr_t)(__base + GC_max(size, __stack)); +} +#endif /* 0 */ + +# else /* !AMIGA, !OS2, ... */ + +# ifdef NEED_FIND_LIMIT + /* Some tools to implement HEURISTIC2 */ +# define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ + /* static */ jmp_buf GC_jmp_buf; + + /*ARGSUSED*/ + void GC_fault_handler(sig) + int sig; + { + longjmp(GC_jmp_buf, 1); + } + +# ifdef __STDC__ + typedef void (*handler)(int); +# else + typedef void (*handler)(); +# endif + +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) + static struct sigaction old_segv_act; +# if defined(_sigargs) /* !Irix6.x */ + static struct sigaction old_bus_act; +# endif +# else + static handler old_segv_handler, old_bus_handler; +# endif + + void GC_setup_temporary_fault_handler() + { +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) + struct sigaction act; + + act.sa_handler = GC_fault_handler; + act.sa_flags = SA_RESTART | SA_NODEFER; + /* The presence of SA_NODEFER represents yet another gross */ + /* hack. Under Solaris 2.3, siglongjmp doesn't appear to */ + /* interact correctly with -lthread. We hide the confusion */ + /* by making sure that signal handling doesn't affect the */ + /* signal mask. */ + + (void) sigemptyset(&act.sa_mask); +# ifdef IRIX_THREADS + /* Older versions have a bug related to retrieving and */ + /* and setting a handler at the same time. */ + (void) sigaction(SIGSEGV, 0, &old_segv_act); + (void) sigaction(SIGSEGV, &act, 0); +# else + (void) sigaction(SIGSEGV, &act, &old_segv_act); +# ifdef _sigargs /* Irix 5.x, not 6.x */ + /* Under 5.x, we may get SIGBUS. */ + /* Pthreads doesn't exist under 5.x, so we don't */ + /* have to worry in the threads case. */ + (void) sigaction(SIGBUS, &act, &old_bus_act); +# endif +# endif /* IRIX_THREADS */ +# else + old_segv_handler = signal(SIGSEGV, GC_fault_handler); +# ifdef SIGBUS + old_bus_handler = signal(SIGBUS, GC_fault_handler); +# endif +# endif + } + + void GC_reset_fault_handler() + { +# if defined(SUNOS5SIGS) || defined(IRIX5) || defined(OSF1) + (void) sigaction(SIGSEGV, &old_segv_act, 0); +# ifdef _sigargs /* Irix 5.x, not 6.x */ + (void) sigaction(SIGBUS, &old_bus_act, 0); +# endif +# else + (void) signal(SIGSEGV, old_segv_handler); +# ifdef SIGBUS + (void) signal(SIGBUS, old_bus_handler); +# endif +# endif + } + + /* Return the first nonaddressible location > p (up) or */ + /* the smallest location q s.t. [q,p] is addressible (!up). */ + ptr_t GC_find_limit(p, up) + ptr_t p; + GC_bool up; + { + static VOLATILE ptr_t result; + /* Needs to be static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called once, with the */ + /* allocation lock held. */ + + + GC_setup_temporary_fault_handler(); + if (setjmp(GC_jmp_buf) == 0) { + result = (ptr_t)(((word)(p)) + & ~(MIN_PAGE_SIZE-1)); + for (;;) { + if (up) { + result += MIN_PAGE_SIZE; + } else { + result -= MIN_PAGE_SIZE; + } + GC_noop1((word)(*result)); + } + } + GC_reset_fault_handler(); + if (!up) { + result += MIN_PAGE_SIZE; + } + return(result); + } +# endif + + +ptr_t GC_get_stack_base() +{ + word dummy; + ptr_t result; + +# define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) + +# ifdef STACKBOTTOM + return(STACKBOTTOM); +# else +# ifdef HEURISTIC1 +# ifdef STACK_GROWS_DOWN + result = (ptr_t)((((word)(&dummy)) + + STACKBOTTOM_ALIGNMENT_M1) + & ~STACKBOTTOM_ALIGNMENT_M1); +# else + result = (ptr_t)(((word)(&dummy)) + & ~STACKBOTTOM_ALIGNMENT_M1); +# endif +# endif /* HEURISTIC1 */ +# ifdef HEURISTIC2 +# ifdef STACK_GROWS_DOWN + result = GC_find_limit((ptr_t)(&dummy), TRUE); +# ifdef HEURISTIC2_LIMIT + if (result > HEURISTIC2_LIMIT + && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# else + result = GC_find_limit((ptr_t)(&dummy), FALSE); +# ifdef HEURISTIC2_LIMIT + if (result < HEURISTIC2_LIMIT + && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# endif + +# endif /* HEURISTIC2 */ +# ifdef STACK_GROWS_DOWN + if (result == 0) result = (ptr_t)(signed_word)(-sizeof(ptr_t)); +# endif + return(result); +# endif /* STACKBOTTOM */ +} + +# endif /* ! AMIGA */ +# endif /* ! OS2 */ +# endif /* ! MSWIN32 */ + +/* + * Register static data segment(s) as roots. + * If more data segments are added later then they need to be registered + * add that point (as we do with SunOS dynamic loading), + * or GC_mark_roots needs to check for them (as we do with PCR). + * Called with allocator lock held. + */ + +# ifdef OS2 + +void GC_register_data_segments() +{ + PTIB ptib; + PPIB ppib; + HMODULE module_handle; +# define PBUFSIZ 512 + UCHAR path[PBUFSIZ]; + FILE * myexefile; + struct exe_hdr hdrdos; /* MSDOS header. */ + struct e32_exe hdr386; /* Real header for my executable */ + struct o32_obj seg; /* Currrent segment */ + int nsegs; + + + if (DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) { + GC_err_printf0("DosGetInfoBlocks failed\n"); + ABORT("DosGetInfoBlocks failed\n"); + } + module_handle = ppib -> pib_hmte; + if (DosQueryModuleName(module_handle, PBUFSIZ, path) != NO_ERROR) { + GC_err_printf0("DosQueryModuleName failed\n"); + ABORT("DosGetInfoBlocks failed\n"); + } + myexefile = fopen(path, "rb"); + if (myexefile == 0) { + GC_err_puts("Couldn't open executable "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Failed to open executable\n"); + } + if (fread((char *)(&hdrdos), 1, sizeof hdrdos, myexefile) < sizeof hdrdos) { + GC_err_puts("Couldn't read MSDOS header from "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Couldn't read MSDOS header"); + } + if (E_MAGIC(hdrdos) != EMAGIC) { + GC_err_puts("Executable has wrong DOS magic number: "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Bad DOS magic number"); + } + if (fseek(myexefile, E_LFANEW(hdrdos), SEEK_SET) != 0) { + GC_err_puts("Seek to new header failed in "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Bad DOS magic number"); + } + if (fread((char *)(&hdr386), 1, sizeof hdr386, myexefile) < sizeof hdr386) { + GC_err_puts("Couldn't read MSDOS header from "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Couldn't read OS/2 header"); + } + if (E32_MAGIC1(hdr386) != E32MAGIC1 || E32_MAGIC2(hdr386) != E32MAGIC2) { + GC_err_puts("Executable has wrong OS/2 magic number:"); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Bad OS/2 magic number"); + } + if ( E32_BORDER(hdr386) != E32LEBO || E32_WORDER(hdr386) != E32LEWO) { + GC_err_puts("Executable %s has wrong byte order: "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Bad byte order"); + } + if ( E32_CPU(hdr386) == E32CPU286) { + GC_err_puts("GC can't handle 80286 executables: "); + GC_err_puts(path); GC_err_puts("\n"); + EXIT(); + } + if (fseek(myexefile, E_LFANEW(hdrdos) + E32_OBJTAB(hdr386), + SEEK_SET) != 0) { + GC_err_puts("Seek to object table failed: "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Seek to object table failed"); + } + for (nsegs = E32_OBJCNT(hdr386); nsegs > 0; nsegs--) { + int flags; + if (fread((char *)(&seg), 1, sizeof seg, myexefile) < sizeof seg) { + GC_err_puts("Couldn't read obj table entry from "); + GC_err_puts(path); GC_err_puts("\n"); + ABORT("Couldn't read obj table entry"); + } + flags = O32_FLAGS(seg); + if (!(flags & OBJWRITE)) continue; + if (!(flags & OBJREAD)) continue; + if (flags & OBJINVALID) { + GC_err_printf0("Object with invalid pages?\n"); + continue; + } + GC_add_roots_inner(O32_BASE(seg), O32_BASE(seg)+O32_SIZE(seg), FALSE); + } +} + +# else + +# ifdef MSWIN32 + /* Unfortunately, we have to handle win32s very differently from NT, */ + /* Since VirtualQuery has very different semantics. In particular, */ + /* under win32s a VirtualQuery call on an unmapped page returns an */ + /* invalid result. Under GC_register_data_segments is a noop and */ + /* all real work is done by GC_register_dynamic_libraries. Under */ + /* win32s, we cannot find the data segments associated with dll's. */ + /* We rgister the main data segment here. */ + GC_bool GC_win32s = FALSE; /* We're running under win32s. */ + + GC_bool GC_is_win32s() + { + DWORD v = GetVersion(); + + /* Check that this is not NT, and Windows major version <= 3 */ + return ((v & 0x80000000) && (v & 0xff) <= 3); + } + + void GC_init_win32() + { + GC_win32s = GC_is_win32s(); + } + + /* Return the smallest address a such that VirtualQuery */ + /* returns correct results for all addresses between a and start. */ + /* Assumes VirtualQuery returns correct information for start. */ + ptr_t GC_least_described_address(ptr_t start) + { + MEMORY_BASIC_INFORMATION buf; + SYSTEM_INFO sysinfo; + DWORD result; + LPVOID limit; + ptr_t p; + LPVOID q; + + GetSystemInfo(&sysinfo); + limit = sysinfo.lpMinimumApplicationAddress; + p = (ptr_t)((word)start & ~(GC_page_size - 1)); + for (;;) { + q = (LPVOID)(p - GC_page_size); + if ((ptr_t)q > (ptr_t)p /* underflow */ || q < limit) break; + result = VirtualQuery(q, &buf, sizeof(buf)); + if (result != sizeof(buf) || buf.AllocationBase == 0) break; + p = (ptr_t)(buf.AllocationBase); + } + return(p); + } + + /* Is p the start of either the malloc heap, or of one of our */ + /* heap sections? */ + GC_bool GC_is_heap_base (ptr_t p) + { + + register unsigned i; + +# ifndef REDIRECT_MALLOC + static ptr_t malloc_heap_pointer = 0; + + if (0 == malloc_heap_pointer) { + MEMORY_BASIC_INFORMATION buf; + register DWORD result = VirtualQuery(malloc(1), &buf, sizeof(buf)); + + if (result != sizeof(buf)) { + ABORT("Weird VirtualQuery result"); + } + malloc_heap_pointer = (ptr_t)(buf.AllocationBase); + } + if (p == malloc_heap_pointer) return(TRUE); +# endif + for (i = 0; i < GC_n_heap_bases; i++) { + if (GC_heap_bases[i] == p) return(TRUE); + } + return(FALSE); + } + + void GC_register_root_section(ptr_t static_root) + { + MEMORY_BASIC_INFORMATION buf; + SYSTEM_INFO sysinfo; + DWORD result; + DWORD protect; + LPVOID p; + char * base; + char * limit, * new_limit; + + if (!GC_win32s) return; + p = base = limit = GC_least_described_address(static_root); + GetSystemInfo(&sysinfo); + while (p < sysinfo.lpMaximumApplicationAddress) { + result = VirtualQuery(p, &buf, sizeof(buf)); + if (result != sizeof(buf) || buf.AllocationBase == 0 + || GC_is_heap_base(buf.AllocationBase)) break; + new_limit = (char *)p + buf.RegionSize; + protect = buf.Protect; + if (buf.State == MEM_COMMIT + && is_writable(protect)) { + if ((char *)p == limit) { + limit = new_limit; + } else { + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + base = p; + limit = new_limit; + } + } + if (p > (LPVOID)new_limit /* overflow */) break; + p = (LPVOID)new_limit; + } + if (base != limit) GC_add_roots_inner(base, limit, FALSE); + } + + void GC_register_data_segments() + { + static char dummy; + + GC_register_root_section((ptr_t)(&dummy)); + } +# else +# ifdef AMIGA + + void GC_register_data_segments() + { + struct Process *proc; + struct CommandLineInterface *cli; + BPTR myseglist; + ULONG *data; + + int num; + + +# ifdef __GNUC__ + ULONG dataSegSize; + GC_bool found_segment = FALSE; + extern char __data_size[]; + + dataSegSize=__data_size+8; + /* Can`t find the Location of __data_size, because + it`s possible that is it, inside the segment. */ + +# endif + + proc= (struct Process*)SysBase->ThisTask; + + /* Reference: Amiga Guru Book Pages: 538ff,565,573 + and XOper.asm */ + if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS) { + if (proc->pr_CLI == NULL) { + myseglist = proc->pr_SegList; + } else { + /* ProcLoaded 'Loaded as a command: '*/ + cli = BADDR(proc->pr_CLI); + myseglist = cli->cli_Module; + } + } else { + ABORT("Not a Process."); + } + + if (myseglist == NULL) { + ABORT("Arrrgh.. can't find segments, aborting"); + } + + /* xoper hunks Shell Process */ + + num=0; + for (data = (ULONG *)BADDR(myseglist); data != NULL; + data = (ULONG *)BADDR(data[0])) { + if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) || + ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) { +# ifdef __GNUC__ + if (dataSegSize == data[-1]) { + found_segment = TRUE; + } +# endif + GC_add_roots_inner((char *)&data[1], + ((char *)&data[1]) + data[-1], FALSE); + } + ++num; + } /* for */ +# ifdef __GNUC__ + if (!found_segment) { + ABORT("Can`t find correct Segments.\nSolution: Use an newer version of ixemul.library"); + } +# endif + } + +#if 0 /* old version */ + void GC_register_data_segments() + { + extern struct WBStartup *_WBenchMsg; + struct Process *proc; + struct CommandLineInterface *cli; + BPTR myseglist; + ULONG *data; + + if ( _WBenchMsg != 0 ) { + if ((myseglist = _WBenchMsg->sm_Segment) == 0) { + GC_err_puts("No seglist from workbench\n"); + return; + } + } else { + if ((proc = (struct Process *)FindTask(0)) == 0) { + GC_err_puts("Cannot find process structure\n"); + return; + } + if ((cli = BADDR(proc->pr_CLI)) == 0) { + GC_err_puts("No CLI\n"); + return; + } + if ((myseglist = cli->cli_Module) == 0) { + GC_err_puts("No seglist from CLI\n"); + return; + } + } + + for (data = (ULONG *)BADDR(myseglist); data != 0; + data = (ULONG *)BADDR(data[0])) { +# ifdef AMIGA_SKIP_SEG + if (((ULONG) GC_register_data_segments < (ULONG) &data[1]) || + ((ULONG) GC_register_data_segments > (ULONG) &data[1] + data[-1])) { +# else + { +# endif /* AMIGA_SKIP_SEG */ + GC_add_roots_inner((char *)&data[1], + ((char *)&data[1]) + data[-1], FALSE); + } + } + } +#endif /* old version */ + + +# else + +# if (defined(SVR4) || defined(AUX) || defined(DGUX)) && !defined(PCR) +char * GC_SysVGetDataStart(max_page_size, etext_addr) +int max_page_size; +int * etext_addr; +{ + word text_end = ((word)(etext_addr) + sizeof(word) - 1) + & ~(sizeof(word) - 1); + /* etext rounded to word boundary */ + word next_page = ((text_end + (word)max_page_size - 1) + & ~((word)max_page_size - 1)); + word page_offset = (text_end & ((word)max_page_size - 1)); + VOLATILE char * result = (char *)(next_page + page_offset); + /* Note that this isnt equivalent to just adding */ + /* max_page_size to &etext if &etext is at a page boundary */ + + GC_setup_temporary_fault_handler(); + if (setjmp(GC_jmp_buf) == 0) { + /* Try writing to the address. */ + *result = *result; + GC_reset_fault_handler(); + } else { + GC_reset_fault_handler(); + /* We got here via a longjmp. The address is not readable. */ + /* This is known to happen under Solaris 2.4 + gcc, which place */ + /* string constants in the text segment, but after etext. */ + /* Use plan B. Note that we now know there is a gap between */ + /* text and data segments, so plan A bought us something. */ + result = (char *)GC_find_limit((ptr_t)(DATAEND) - MIN_PAGE_SIZE, FALSE); + } + return((char *)result); +} +# endif + + +void GC_register_data_segments() +{ +# if !defined(PCR) && !defined(SRC_M3) && !defined(NEXT) && !defined(MACOS) \ + && !defined(MACOSX) +# if defined(REDIRECT_MALLOC) && defined(SOLARIS_THREADS) + /* As of Solaris 2.3, the Solaris threads implementation */ + /* allocates the data structure for the initial thread with */ + /* sbrk at process startup. It needs to be scanned, so that */ + /* we don't lose some malloc allocated data structures */ + /* hanging from it. We're on thin ice here ... */ + extern caddr_t sbrk(); + + GC_add_roots_inner(DATASTART, (char *)sbrk(0), FALSE); +# else + GC_add_roots_inner(DATASTART, (char *)(DATAEND), FALSE); +# endif +# endif +# if !defined(PCR) && (defined(NEXT) || defined(MACOSX)) + GC_add_roots_inner(DATASTART, (char *) get_end(), FALSE); +# endif +# if defined(MACOS) + { +# if defined(THINK_C) + extern void* GC_MacGetDataStart(void); + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); +# else +# if defined(__MWERKS__) +# if !__POWERPC__ + extern void* GC_MacGetDataStart(void); + /* MATTHEW: Function to handle Far Globals (CW Pro 3) */ +# if __option(far_data) + extern void* GC_MacGetDataEnd(void); +# endif + /* globals begin above stack and end at a5. */ + GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), + (ptr_t)LMGetCurrentA5(), FALSE); + /* MATTHEW: Handle Far Globals */ +# if __option(far_data) + /* Far globals follow he QD globals: */ + GC_add_roots_inner((ptr_t)LMGetCurrentA5(), + (ptr_t)GC_MacGetDataEnd(), FALSE); +# endif +# else + extern char __data_start__[], __data_end__[]; + GC_add_roots_inner((ptr_t)&__data_start__, + (ptr_t)&__data_end__, FALSE); +# endif /* __POWERPC__ */ +# endif /* __MWERKS__ */ +# endif /* !THINK_C */ + } +# endif /* MACOS */ + + /* Dynamic libraries are added at every collection, since they may */ + /* change. */ +} + +# endif /* ! AMIGA */ +# endif /* ! MSWIN32 */ +# endif /* ! OS2 */ + +/* + * Auxiliary routines for obtaining memory from OS. + */ + +# if !defined(OS2) && !defined(PCR) && !defined(AMIGA) \ + && !defined(MSWIN32) && !defined(MACOS) && !defined(DOS4GW) + +# ifdef SUNOS4 + extern caddr_t sbrk(); +# endif +# ifdef __STDC__ +# define SBRK_ARG_T ptrdiff_t +# else +# define SBRK_ARG_T int +# endif + +# ifdef RS6000 +/* The compiler seems to generate speculative reads one past the end of */ +/* an allocated object. Hence we need to make sure that the page */ +/* following the last heap page is also mapped. */ +ptr_t GC_unix_get_mem(bytes) +word bytes; +{ + caddr_t cur_brk = (caddr_t)sbrk(0); + caddr_t result; + SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); + static caddr_t my_brk_val = 0; + + if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */ + if (lsbs != 0) { + if((caddr_t)(sbrk(GC_page_size - lsbs)) == (caddr_t)(-1)) return(0); + } + if (cur_brk == my_brk_val) { + /* Use the extra block we allocated last time. */ + result = (ptr_t)sbrk((SBRK_ARG_T)bytes); + if (result == (caddr_t)(-1)) return(0); + result -= GC_page_size; + } else { + result = (ptr_t)sbrk(GC_page_size + (SBRK_ARG_T)bytes); + if (result == (caddr_t)(-1)) return(0); + } + my_brk_val = result + bytes + GC_page_size; /* Always page aligned */ + return((ptr_t)result); +} + +#else /* Not RS6000 */ + +#if defined(USE_MMAP) +/* Tested only under IRIX5 and Solaris 2 */ + +#ifdef USE_MMAP_FIXED +# define GC_MMAP_FLAGS MAP_FIXED | MAP_PRIVATE + /* Seems to yield better performance on Solaris 2, but can */ + /* be unreliable if something is already mapped at the address. */ +#else +# define GC_MMAP_FLAGS MAP_PRIVATE +#endif + +ptr_t GC_unix_get_mem(bytes) +word bytes; +{ + static GC_bool initialized = FALSE; + static int fd; + void *result; + static ptr_t last_addr = HEAP_START; + + if (!initialized) { + fd = open("/dev/zero", O_RDONLY); + initialized = TRUE; + } + if (bytes & (GC_page_size -1)) ABORT("Bad GET_MEM arg"); + result = mmap(last_addr, bytes, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, + GC_MMAP_FLAGS, fd, 0/* offset */); + if (result == MAP_FAILED) return(0); + last_addr = (ptr_t)result + bytes + GC_page_size - 1; + last_addr = (ptr_t)((word)last_addr & ~(GC_page_size - 1)); + return((ptr_t)result); +} + +#else /* Not RS6000, not USE_MMAP */ +ptr_t GC_unix_get_mem(bytes) +word bytes; +{ + ptr_t result; +# ifdef IRIX5 + /* Bare sbrk isn't thread safe. Play by malloc rules. */ + /* The equivalent may be needed on other systems as well. */ + __LOCK_MALLOC(); +# endif + { + ptr_t cur_brk = (ptr_t)sbrk(0); + SBRK_ARG_T lsbs = (word)cur_brk & (GC_page_size-1); + + if ((SBRK_ARG_T)bytes < 0) return(0); /* too big */ + if (lsbs != 0) { + if((ptr_t)sbrk(GC_page_size - lsbs) == (ptr_t)(-1)) return(0); + } + result = (ptr_t)sbrk((SBRK_ARG_T)bytes); + if (result == (ptr_t)(-1)) result = 0; + } +# ifdef IRIX5 + __UNLOCK_MALLOC(); +# endif + return(result); +} + +#endif /* Not USE_MMAP */ +#endif /* Not RS6000 */ + +# endif /* UN*X */ + +# ifdef OS2 + +void * os2_alloc(size_t bytes) +{ + void * result; + + if (DosAllocMem(&result, bytes, PAG_EXECUTE | PAG_READ | + PAG_WRITE | PAG_COMMIT) + != NO_ERROR) { + return(0); + } + if (result == 0) return(os2_alloc(bytes)); + return(result); +} + +# endif /* OS2 */ + + +# ifdef MSWIN32 +word GC_n_heap_bases = 0; + +ptr_t GC_win32_get_mem(bytes) +word bytes; +{ + ptr_t result; + + if (GC_win32s) { + /* VirtualAlloc doesn't like PAGE_EXECUTE_READWRITE. */ + /* There are also unconfirmed rumors of other */ + /* problems, so we dodge the issue. */ + result = (ptr_t) GlobalAlloc(0, bytes + HBLKSIZE); + result = (ptr_t)(((word)result + HBLKSIZE) & ~(HBLKSIZE-1)); + } else { + result = (ptr_t) VirtualAlloc(NULL, bytes, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE); + } + if (HBLKDISPL(result) != 0) ABORT("Bad VirtualAlloc result"); + /* If I read the documentation correctly, this can */ + /* only happen if HBLKSIZE > 64k or not a power of 2. */ + if (GC_n_heap_bases >= MAX_HEAP_SECTS) ABORT("Too many heap sections"); + GC_heap_bases[GC_n_heap_bases++] = result; + return(result); +} + +void GC_win32_free_heap () +{ + if (GC_win32s) { + while (GC_n_heap_bases > 0) { + GlobalFree (GC_heap_bases[--GC_n_heap_bases]); + GC_heap_bases[GC_n_heap_bases] = 0; + } + } +} + + +# endif + +#ifdef USE_MUNMAP + +/* For now, this only works on some Unix-like systems. If you */ +/* have something else, don't define USE_MUNMAP. */ +/* We assume ANSI C to support this feature. */ +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> + +/* Compute a page aligned starting address for the unmap */ +/* operation on a block of size bytes starting at start. */ +/* Return 0 if the block is too small to make this feasible. */ +ptr_t GC_unmap_start(ptr_t start, word bytes) +{ + ptr_t result = start; + /* Round start to next page boundary. */ + result += GC_page_size - 1; + result = (ptr_t)((word)result & ~(GC_page_size - 1)); + if (result + GC_page_size > start + bytes) return 0; + return result; +} + +/* Compute end address for an unmap operation on the indicated */ +/* block. */ +ptr_t GC_unmap_end(ptr_t start, word bytes) +{ + ptr_t end_addr = start + bytes; + end_addr = (ptr_t)((word)end_addr & ~(GC_page_size - 1)); + return end_addr; +} + +/* We assume that GC_remap is called on exactly the same range */ +/* as a previous call to GC_unmap. It is safe to consistently */ +/* round the endpoints in both places. */ +void GC_unmap(ptr_t start, word bytes) +{ + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + word len = end_addr - start_addr; + if (0 == start_addr) return; + if (munmap(start_addr, len) != 0) ABORT("munmap failed"); + GC_unmapped_bytes += len; +} + + +void GC_remap(ptr_t start, word bytes) +{ + static int zero_descr = -1; + ptr_t start_addr = GC_unmap_start(start, bytes); + ptr_t end_addr = GC_unmap_end(start, bytes); + word len = end_addr - start_addr; + ptr_t result; + + if (-1 == zero_descr) zero_descr = open("/dev/zero", O_RDWR); + if (0 == start_addr) return; + result = mmap(start_addr, len, PROT_READ | PROT_WRITE | OPT_PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, zero_descr, 0); + if (result != start_addr) { + ABORT("mmap remapping failed"); + } + GC_unmapped_bytes -= len; +} + +/* Two adjacent blocks have already been unmapped and are about to */ +/* be merged. Unmap the whole block. This typically requires */ +/* that we unmap a small section in the middle that was not previously */ +/* unmapped due to alignment constraints. */ +void GC_unmap_gap(ptr_t start1, word bytes1, ptr_t start2, word bytes2) +{ + ptr_t start1_addr = GC_unmap_start(start1, bytes1); + ptr_t end1_addr = GC_unmap_end(start1, bytes1); + ptr_t start2_addr = GC_unmap_start(start2, bytes2); + ptr_t end2_addr = GC_unmap_end(start2, bytes2); + ptr_t start_addr = end1_addr; + ptr_t end_addr = start2_addr; + word len; + GC_ASSERT(start1 + bytes1 == start2); + if (0 == start1_addr) start_addr = GC_unmap_start(start1, bytes1 + bytes2); + if (0 == start2_addr) end_addr = GC_unmap_end(start1, bytes1 + bytes2); + if (0 == start_addr) return; + len = end_addr - start_addr; + if (len != 0 && munmap(start_addr, len) != 0) ABORT("munmap failed"); + GC_unmapped_bytes += len; +} + +#endif /* USE_MUNMAP */ + +/* Routine for pushing any additional roots. In THREADS */ +/* environment, this is also responsible for marking from */ +/* thread stacks. In the SRC_M3 case, it also handles */ +/* global variables. */ +#ifndef THREADS +void (*GC_push_other_roots)() = 0; +#else /* THREADS */ + +# ifdef PCR +PCR_ERes GC_push_thread_stack(PCR_Th_T *t, PCR_Any dummy) +{ + struct PCR_ThCtl_TInfoRep info; + PCR_ERes result; + + info.ti_stkLow = info.ti_stkHi = 0; + result = PCR_ThCtl_GetInfo(t, &info); + GC_push_all_stack((ptr_t)(info.ti_stkLow), (ptr_t)(info.ti_stkHi)); + return(result); +} + +/* Push the contents of an old object. We treat this as stack */ +/* data only becasue that makes it robust against mark stack */ +/* overflow. */ +PCR_ERes GC_push_old_obj(void *p, size_t size, PCR_Any data) +{ + GC_push_all_stack((ptr_t)p, (ptr_t)p + size); + return(PCR_ERes_okay); +} + + +void GC_default_push_other_roots() +{ + /* Traverse data allocated by previous memory managers. */ + { + extern struct PCR_MM_ProcsRep * GC_old_allocator; + + if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false, + GC_push_old_obj, 0) + != PCR_ERes_okay) { + ABORT("Old object enumeration failed"); + } + } + /* Traverse all thread stacks. */ + if (PCR_ERes_IsErr( + PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) + || PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(), 0))) { + ABORT("Thread stack marking failed\n"); + } +} + +# endif /* PCR */ + +# ifdef SRC_M3 + +# ifdef ALL_INTERIOR_POINTERS + --> misconfigured +# endif + + +extern void ThreadF__ProcessStacks(); + +void GC_push_thread_stack(start, stop) +word start, stop; +{ + GC_push_all_stack((ptr_t)start, (ptr_t)stop + sizeof(word)); +} + +/* Push routine with M3 specific calling convention. */ +GC_m3_push_root(dummy1, p, dummy2, dummy3) +word *p; +ptr_t dummy1, dummy2; +int dummy3; +{ + word q = *p; + + if ((ptr_t)(q) >= GC_least_plausible_heap_addr + && (ptr_t)(q) < GC_greatest_plausible_heap_addr) { + GC_push_one_checked(q,FALSE); + } +} + +/* M3 set equivalent to RTHeap.TracedRefTypes */ +typedef struct { int elts[1]; } RefTypeSet; +RefTypeSet GC_TracedRefTypes = {{0x1}}; + +/* From finalize.c */ +extern void GC_push_finalizer_structures(); + +/* From stubborn.c: */ +# ifdef STUBBORN_ALLOC + extern GC_PTR * GC_changing_list_start; +# endif + + +void GC_default_push_other_roots() +{ + /* Use the M3 provided routine for finding static roots. */ + /* This is a bit dubious, since it presumes no C roots. */ + /* We handle the collector roots explicitly. */ + { +# ifdef STUBBORN_ALLOC + GC_push_one(GC_changing_list_start); +# endif + GC_push_finalizer_structures(); + RTMain__GlobalMapProc(GC_m3_push_root, 0, GC_TracedRefTypes); + } + if (GC_words_allocd > 0) { + ThreadF__ProcessStacks(GC_push_thread_stack); + } + /* Otherwise this isn't absolutely necessary, and we have */ + /* startup ordering problems. */ +} + +# endif /* SRC_M3 */ + +# if defined(SOLARIS_THREADS) || defined(WIN32_THREADS) \ + || defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(IRIX_PCR_THREADS) + +extern void GC_push_all_stacks(); + +void GC_default_push_other_roots() +{ + GC_push_all_stacks(); +} + +# endif /* SOLARIS_THREADS || ... */ + +void (*GC_push_other_roots)() = GC_default_push_other_roots; + +#endif + +/* + * Routines for accessing dirty bits on virtual pages. + * We plan to eventaually implement four strategies for doing so: + * DEFAULT_VDB: A simple dummy implementation that treats every page + * as possibly dirty. This makes incremental collection + * useless, but the implementation is still correct. + * PCR_VDB: Use PPCRs virtual dirty bit facility. + * PROC_VDB: Use the /proc facility for reading dirty bits. Only + * works under some SVR4 variants. Even then, it may be + * too slow to be entirely satisfactory. Requires reading + * dirty bits for entire address space. Implementations tend + * to assume that the client is a (slow) debugger. + * MPROTECT_VDB:Protect pages and then catch the faults to keep track of + * dirtied pages. The implementation (and implementability) + * is highly system dependent. This usually fails when system + * calls write to a protected page. We prevent the read system + * call from doing so. It is the clients responsibility to + * make sure that other system calls are similarly protected + * or write only to the stack. + */ + +GC_bool GC_dirty_maintained = FALSE; + +# ifdef DEFAULT_VDB + +/* All of the following assume the allocation lock is held, and */ +/* signals are disabled. */ + +/* The client asserts that unallocated pages in the heap are never */ +/* written. */ + +/* Initialize virtual dirty bit implementation. */ +void GC_dirty_init() +{ + GC_dirty_maintained = TRUE; +} + +/* Retrieve system dirty bits for heap to a local buffer. */ +/* Restore the systems notion of which pages are dirty. */ +void GC_read_dirty() +{} + +/* Is the HBLKSIZE sized page at h marked dirty in the local buffer? */ +/* If the actual page size is different, this returns TRUE if any */ +/* of the pages overlapping h are dirty. This routine may err on the */ +/* side of labelling pages as dirty (and this implementation does). */ +/*ARGSUSED*/ +GC_bool GC_page_was_dirty(h) +struct hblk *h; +{ + return(TRUE); +} + +/* + * The following two routines are typically less crucial. They matter + * most with large dynamic libraries, or if we can't accurately identify + * stacks, e.g. under Solaris 2.X. Otherwise the following default + * versions are adequate. + */ + +/* Could any valid GC heap pointer ever have been written to this page? */ +/*ARGSUSED*/ +GC_bool GC_page_was_ever_dirty(h) +struct hblk *h; +{ + return(TRUE); +} + +/* Reset the n pages starting at h to "was never dirty" status. */ +void GC_is_fresh(h, n) +struct hblk *h; +word n; +{ +} + +/* A call hints that h is about to be written. */ +/* May speed up some dirty bit implementations. */ +/*ARGSUSED*/ +void GC_write_hint(h) +struct hblk *h; +{ +} + +# endif /* DEFAULT_VDB */ + + +# ifdef MPROTECT_VDB + +/* + * See DEFAULT_VDB for interface descriptions. + */ + +/* + * This implementation maintains dirty bits itself by catching write + * faults and keeping track of them. We assume nobody else catches + * SIGBUS or SIGSEGV. We assume no write faults occur in system calls + * except as a result of a read system call. This means clients must + * either ensure that system calls do not touch the heap, or must + * provide their own wrappers analogous to the one for read. + * We assume the page size is a multiple of HBLKSIZE. + * This implementation is currently SunOS 4.X and IRIX 5.X specific, though we + * tried to use portable code where easily possible. It is known + * not to work under a number of other systems. + */ + +# ifndef MSWIN32 + +# include <sys/mman.h> +# include <signal.h> +# include <sys/syscall.h> + +# define PROTECT(addr, len) \ + if (mprotect((caddr_t)(addr), (int)(len), \ + PROT_READ | OPT_PROT_EXEC) < 0) { \ + ABORT("mprotect failed"); \ + } +# define UNPROTECT(addr, len) \ + if (mprotect((caddr_t)(addr), (int)(len), \ + PROT_WRITE | PROT_READ | OPT_PROT_EXEC ) < 0) { \ + ABORT("un-mprotect failed"); \ + } + +# else + +# include <signal.h> + + static DWORD protect_junk; +# define PROTECT(addr, len) \ + if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READ, \ + &protect_junk)) { \ + DWORD last_error = GetLastError(); \ + GC_printf1("Last error code: %lx\n", last_error); \ + ABORT("VirtualProtect failed"); \ + } +# define UNPROTECT(addr, len) \ + if (!VirtualProtect((addr), (len), PAGE_EXECUTE_READWRITE, \ + &protect_junk)) { \ + ABORT("un-VirtualProtect failed"); \ + } + +# endif + +#if defined(SUNOS4) || defined(FREEBSD) + typedef void (* SIG_PF)(); +#endif +#if defined(SUNOS5SIGS) || defined(OSF1) || defined(LINUX) + typedef void (* SIG_PF)(int); +#endif +#if defined(MSWIN32) + typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_PF; +# undef SIG_DFL +# define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER) (-1) +#endif + +#if defined(IRIX5) || defined(OSF1) + typedef void (* REAL_SIG_PF)(int, int, struct sigcontext *); +#endif +#if defined(SUNOS5SIGS) + typedef void (* REAL_SIG_PF)(int, struct siginfo *, void *); +#endif +#if defined(LINUX) +# include <linux/version.h> +# if (LINUX_VERSION_CODE >= 0x20100) && !defined(M68K) || defined(ALPHA) + typedef struct sigcontext s_c; +# else + typedef struct sigcontext_struct s_c; +# endif +# ifdef ALPHA + typedef void (* REAL_SIG_PF)(int, int, s_c *); + /* Retrieve fault address from sigcontext structure by decoding */ + /* instruction. */ + char * get_fault_addr(s_c *sc) { + unsigned instr; + word faultaddr; + + instr = *((unsigned *)(sc->sc_pc)); + faultaddr = sc->sc_regs[(instr >> 16) & 0x1f]; + faultaddr += (word) (((int)instr << 16) >> 16); + return (char *)faultaddr; + } +# else /* !ALPHA */ + typedef void (* REAL_SIG_PF)(int, s_c); +# endif /* !ALPHA */ +# endif + +SIG_PF GC_old_bus_handler; +SIG_PF GC_old_segv_handler; /* Also old MSWIN32 ACCESS_VIOLATION filter */ + +/*ARGSUSED*/ +# if defined (SUNOS4) || defined(FREEBSD) + void GC_write_fault_handler(sig, code, scp, addr) + int sig, code; + struct sigcontext *scp; + char * addr; +# ifdef SUNOS4 +# define SIG_OK (sig == SIGSEGV || sig == SIGBUS) +# define CODE_OK (FC_CODE(code) == FC_PROT \ + || (FC_CODE(code) == FC_OBJERR \ + && FC_ERRNO(code) == FC_PROT)) +# endif +# ifdef FREEBSD +# define SIG_OK (sig == SIGBUS) +# define CODE_OK (code == BUS_PAGE_FAULT) +# endif +# endif +# if defined(IRIX5) || defined(OSF1) +# include <errno.h> + void GC_write_fault_handler(int sig, int code, struct sigcontext *scp) +# define SIG_OK (sig == SIGSEGV) +# ifdef OSF1 +# define CODE_OK (code == 2 /* experimentally determined */) +# endif +# ifdef IRIX5 +# define CODE_OK (code == EACCES) +# endif +# endif +# if defined(LINUX) +# ifdef ALPHA + void GC_write_fault_handler(int sig, int code, s_c * sc) +# else + void GC_write_fault_handler(int sig, s_c sc) +# endif +# define SIG_OK (sig == SIGSEGV) +# define CODE_OK TRUE + /* Empirically c.trapno == 14, but is that useful? */ + /* We assume Intel architecture, so alignment */ + /* faults are not possible. */ +# endif +# if defined(SUNOS5SIGS) + void GC_write_fault_handler(int sig, struct siginfo *scp, void * context) +# define SIG_OK (sig == SIGSEGV) +# define CODE_OK (scp -> si_code == SEGV_ACCERR) +# endif +# if defined(MSWIN32) + LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info) +# define SIG_OK (exc_info -> ExceptionRecord -> ExceptionCode == \ + EXCEPTION_ACCESS_VIOLATION) +# define CODE_OK (exc_info -> ExceptionRecord -> ExceptionInformation[0] == 1) + /* Write fault */ +# endif +{ + register unsigned i; +# ifdef IRIX5 + char * addr = (char *) (size_t) (scp -> sc_badvaddr); +# endif +# if defined(OSF1) && defined(ALPHA) + char * addr = (char *) (scp -> sc_traparg_a0); +# endif +# ifdef SUNOS5SIGS + char * addr = (char *) (scp -> si_addr); +# endif +# ifdef LINUX +# ifdef I386 + char * addr = (char *) (sc.cr2); +# else +# if defined(M68K) + char * addr = NULL; + + struct sigcontext *scp = (struct sigcontext *)(&sc); + + int format = (scp->sc_formatvec >> 12) & 0xf; + unsigned long *framedata = (unsigned long *)(scp + 1); + unsigned long ea; + + if (format == 0xa || format == 0xb) { + /* 68020/030 */ + ea = framedata[2]; + } else if (format == 7) { + /* 68040 */ + ea = framedata[3]; + } else if (format == 4) { + /* 68060 */ + ea = framedata[0]; + if (framedata[1] & 0x08000000) { + /* correct addr on misaligned access */ + ea = (ea+4095)&(~4095); + } + } + addr = (char *)ea; +# else +# ifdef ALPHA + char * addr = get_fault_addr(sc); +# else + --> architecture not supported +# endif +# endif +# endif +# endif +# if defined(MSWIN32) + char * addr = (char *) (exc_info -> ExceptionRecord + -> ExceptionInformation[1]); +# define sig SIGSEGV +# endif + + if (SIG_OK && CODE_OK) { + register struct hblk * h = + (struct hblk *)((word)addr & ~(GC_page_size-1)); + GC_bool in_allocd_block; + +# ifdef SUNOS5SIGS + /* Address is only within the correct physical page. */ + in_allocd_block = FALSE; + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + if (HDR(h+i) != 0) { + in_allocd_block = TRUE; + } + } +# else + in_allocd_block = (HDR(addr) != 0); +# endif + if (!in_allocd_block) { + /* Heap blocks now begin and end on page boundaries */ + SIG_PF old_handler; + + if (sig == SIGSEGV) { + old_handler = GC_old_segv_handler; + } else { + old_handler = GC_old_bus_handler; + } + if (old_handler == SIG_DFL) { +# ifndef MSWIN32 + GC_err_printf1("Segfault at 0x%lx\n", addr); + ABORT("Unexpected bus error or segmentation fault"); +# else + return(EXCEPTION_CONTINUE_SEARCH); +# endif + } else { +# if defined (SUNOS4) || defined(FREEBSD) + (*old_handler) (sig, code, scp, addr); + return; +# endif +# if defined (SUNOS5SIGS) + (*(REAL_SIG_PF)old_handler) (sig, scp, context); + return; +# endif +# if defined (LINUX) +# ifdef ALPHA + (*(REAL_SIG_PF)old_handler) (sig, code, sc); +# else + (*(REAL_SIG_PF)old_handler) (sig, sc); +# endif + return; +# endif +# if defined (IRIX5) || defined(OSF1) + (*(REAL_SIG_PF)old_handler) (sig, code, scp); + return; +# endif +# ifdef MSWIN32 + return((*old_handler)(exc_info)); +# endif + } + } + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + register int index = PHT_HASH(h+i); + + set_pht_entry_from_index(GC_dirty_pages, index); + } + UNPROTECT(h, GC_page_size); +# if defined(OSF1) || defined(LINUX) + /* These reset the signal handler each time by default. */ + signal(SIGSEGV, (SIG_PF) GC_write_fault_handler); +# endif + /* The write may not take place before dirty bits are read. */ + /* But then we'll fault again ... */ +# ifdef MSWIN32 + return(EXCEPTION_CONTINUE_EXECUTION); +# else + return; +# endif + } +#ifdef MSWIN32 + return EXCEPTION_CONTINUE_SEARCH; +#else + GC_err_printf1("Segfault at 0x%lx\n", addr); + ABORT("Unexpected bus error or segmentation fault"); +#endif +} + +/* + * We hold the allocation lock. We expect block h to be written + * shortly. + */ +void GC_write_hint(h) +struct hblk *h; +{ + register struct hblk * h_trunc; + register unsigned i; + register GC_bool found_clean; + + if (!GC_dirty_maintained) return; + h_trunc = (struct hblk *)((word)h & ~(GC_page_size-1)); + found_clean = FALSE; + for (i = 0; i < divHBLKSZ(GC_page_size); i++) { + register int index = PHT_HASH(h_trunc+i); + + if (!get_pht_entry_from_index(GC_dirty_pages, index)) { + found_clean = TRUE; + set_pht_entry_from_index(GC_dirty_pages, index); + } + } + if (found_clean) { + UNPROTECT(h_trunc, GC_page_size); + } +} + +void GC_dirty_init() +{ +#if defined(SUNOS5SIGS) || defined(IRIX5) /* || defined(OSF1) */ + struct sigaction act, oldact; +# ifdef IRIX5 + act.sa_flags = SA_RESTART; + act.sa_handler = GC_write_fault_handler; +# else + act.sa_flags = SA_RESTART | SA_SIGINFO; + act.sa_sigaction = GC_write_fault_handler; +# endif + (void)sigemptyset(&act.sa_mask); +#endif +# ifdef PRINTSTATS + GC_printf0("Inititalizing mprotect virtual dirty bit implementation\n"); +# endif + GC_dirty_maintained = TRUE; + if (GC_page_size % HBLKSIZE != 0) { + GC_err_printf0("Page size not multiple of HBLKSIZE\n"); + ABORT("Page size not multiple of HBLKSIZE"); + } +# if defined(SUNOS4) || defined(FREEBSD) + GC_old_bus_handler = signal(SIGBUS, GC_write_fault_handler); + if (GC_old_bus_handler == SIG_IGN) { + GC_err_printf0("Previously ignored bus error!?"); + GC_old_bus_handler = SIG_DFL; + } + if (GC_old_bus_handler != SIG_DFL) { +# ifdef PRINTSTATS + GC_err_printf0("Replaced other SIGBUS handler\n"); +# endif + } +# endif +# if defined(OSF1) || defined(SUNOS4) || defined(LINUX) + GC_old_segv_handler = signal(SIGSEGV, (SIG_PF)GC_write_fault_handler); + if (GC_old_segv_handler == SIG_IGN) { + GC_err_printf0("Previously ignored segmentation violation!?"); + GC_old_segv_handler = SIG_DFL; + } + if (GC_old_segv_handler != SIG_DFL) { +# ifdef PRINTSTATS + GC_err_printf0("Replaced other SIGSEGV handler\n"); +# endif + } +# endif +# if defined(SUNOS5SIGS) || defined(IRIX5) +# if defined(IRIX_THREADS) || defined(IRIX_PCR_THREADS) + sigaction(SIGSEGV, 0, &oldact); + sigaction(SIGSEGV, &act, 0); +# else + sigaction(SIGSEGV, &act, &oldact); +# endif +# if defined(_sigargs) + /* This is Irix 5.x, not 6.x. Irix 5.x does not have */ + /* sa_sigaction. */ + GC_old_segv_handler = oldact.sa_handler; +# else /* Irix 6.x or SUNOS5SIGS */ + if (oldact.sa_flags & SA_SIGINFO) { + GC_old_segv_handler = (SIG_PF)(oldact.sa_sigaction); + } else { + GC_old_segv_handler = oldact.sa_handler; + } +# endif + if (GC_old_segv_handler == SIG_IGN) { + GC_err_printf0("Previously ignored segmentation violation!?"); + GC_old_segv_handler = SIG_DFL; + } + if (GC_old_segv_handler != SIG_DFL) { +# ifdef PRINTSTATS + GC_err_printf0("Replaced other SIGSEGV handler\n"); +# endif + } +# endif +# if defined(MSWIN32) + GC_old_segv_handler = SetUnhandledExceptionFilter(GC_write_fault_handler); + if (GC_old_segv_handler != NULL) { +# ifdef PRINTSTATS + GC_err_printf0("Replaced other UnhandledExceptionFilter\n"); +# endif + } else { + GC_old_segv_handler = SIG_DFL; + } +# endif +} + + + +void GC_protect_heap() +{ + ptr_t start; + word len; + unsigned i; + + for (i = 0; i < GC_n_heap_sects; i++) { + start = GC_heap_sects[i].hs_start; + len = GC_heap_sects[i].hs_bytes; + PROTECT(start, len); + } +} + +/* We assume that either the world is stopped or its OK to lose dirty */ +/* bits while this is happenning (as in GC_enable_incremental). */ +void GC_read_dirty() +{ + BCOPY((word *)GC_dirty_pages, GC_grungy_pages, + (sizeof GC_dirty_pages)); + BZERO((word *)GC_dirty_pages, (sizeof GC_dirty_pages)); + GC_protect_heap(); +} + +GC_bool GC_page_was_dirty(h) +struct hblk * h; +{ + register word index = PHT_HASH(h); + + return(HDR(h) == 0 || get_pht_entry_from_index(GC_grungy_pages, index)); +} + +/* + * Acquiring the allocation lock here is dangerous, since this + * can be called from within GC_call_with_alloc_lock, and the cord + * package does so. On systems that allow nested lock acquisition, this + * happens to work. + * On other systems, SET_LOCK_HOLDER and friends must be suitably defined. + */ + +void GC_begin_syscall() +{ + if (!I_HOLD_LOCK()) LOCK(); +} + +void GC_end_syscall() +{ + if (!I_HOLD_LOCK()) UNLOCK(); +} + +void GC_unprotect_range(addr, len) +ptr_t addr; +word len; +{ + struct hblk * start_block; + struct hblk * end_block; + register struct hblk *h; + ptr_t obj_start; + + if (!GC_incremental) return; + obj_start = GC_base(addr); + if (obj_start == 0) return; + if (GC_base(addr + len - 1) != obj_start) { + ABORT("GC_unprotect_range(range bigger than object)"); + } + start_block = (struct hblk *)((word)addr & ~(GC_page_size - 1)); + end_block = (struct hblk *)((word)(addr + len - 1) & ~(GC_page_size - 1)); + end_block += GC_page_size/HBLKSIZE - 1; + for (h = start_block; h <= end_block; h++) { + register word index = PHT_HASH(h); + + set_pht_entry_from_index(GC_dirty_pages, index); + } + UNPROTECT(start_block, + ((ptr_t)end_block - (ptr_t)start_block) + HBLKSIZE); +} + +#ifndef MSWIN32 +/* Replacement for UNIX system call. */ +/* Other calls that write to the heap */ +/* should be handled similarly. */ +# if defined(__STDC__) && !defined(SUNOS4) +# include <unistd.h> + ssize_t read(int fd, void *buf, size_t nbyte) +# else +# ifndef LINT + int read(fd, buf, nbyte) +# else + int GC_read(fd, buf, nbyte) +# endif + int fd; + char *buf; + int nbyte; +# endif +{ + int result; + + GC_begin_syscall(); + GC_unprotect_range(buf, (word)nbyte); +# ifdef IRIX5 + /* Indirect system call may not always be easily available. */ + /* We could call _read, but that would interfere with the */ + /* libpthread interception of read. */ + { + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = nbyte; + result = readv(fd, &iov, 1); + } +# else + result = syscall(SYS_read, fd, buf, nbyte); +# endif + GC_end_syscall(); + return(result); +} +#endif /* !MSWIN32 */ + +/*ARGSUSED*/ +GC_bool GC_page_was_ever_dirty(h) +struct hblk *h; +{ + return(TRUE); +} + +/* Reset the n pages starting at h to "was never dirty" status. */ +/*ARGSUSED*/ +void GC_is_fresh(h, n) +struct hblk *h; +word n; +{ +} + +# endif /* MPROTECT_VDB */ + +# ifdef PROC_VDB + +/* + * See DEFAULT_VDB for interface descriptions. + */ + +/* + * This implementaion assumes a Solaris 2.X like /proc pseudo-file-system + * from which we can read page modified bits. This facility is far from + * optimal (e.g. we would like to get the info for only some of the + * address space), but it avoids intercepting system calls. + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/signal.h> +#include <sys/fault.h> +#include <sys/syscall.h> +#include <sys/procfs.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define INITIAL_BUF_SZ 4096 +word GC_proc_buf_size = INITIAL_BUF_SZ; +char *GC_proc_buf; + +#ifdef SOLARIS_THREADS +/* We don't have exact sp values for threads. So we count on */ +/* occasionally declaring stack pages to be fresh. Thus we */ +/* need a real implementation of GC_is_fresh. We can't clear */ +/* entries in GC_written_pages, since that would declare all */ +/* pages with the given hash address to be fresh. */ +# define MAX_FRESH_PAGES 8*1024 /* Must be power of 2 */ + struct hblk ** GC_fresh_pages; /* A direct mapped cache. */ + /* Collisions are dropped. */ + +# define FRESH_PAGE_SLOT(h) (divHBLKSZ((word)(h)) & (MAX_FRESH_PAGES-1)) +# define ADD_FRESH_PAGE(h) \ + GC_fresh_pages[FRESH_PAGE_SLOT(h)] = (h) +# define PAGE_IS_FRESH(h) \ + (GC_fresh_pages[FRESH_PAGE_SLOT(h)] == (h) && (h) != 0) +#endif + +/* Add all pages in pht2 to pht1 */ +void GC_or_pages(pht1, pht2) +page_hash_table pht1, pht2; +{ + register int i; + + for (i = 0; i < PHT_SIZE; i++) pht1[i] |= pht2[i]; +} + +int GC_proc_fd; + +void GC_dirty_init() +{ + int fd; + char buf[30]; + + GC_dirty_maintained = TRUE; + if (GC_words_allocd != 0 || GC_words_allocd_before_gc != 0) { + register int i; + + for (i = 0; i < PHT_SIZE; i++) GC_written_pages[i] = (word)(-1); +# ifdef PRINTSTATS + GC_printf1("Allocated words:%lu:all pages may have been written\n", + (unsigned long) + (GC_words_allocd + GC_words_allocd_before_gc)); +# endif + } + sprintf(buf, "/proc/%d", getpid()); + fd = open(buf, O_RDONLY); + if (fd < 0) { + ABORT("/proc open failed"); + } + GC_proc_fd = syscall(SYS_ioctl, fd, PIOCOPENPD, 0); + close(fd); + if (GC_proc_fd < 0) { + ABORT("/proc ioctl failed"); + } + GC_proc_buf = GC_scratch_alloc(GC_proc_buf_size); +# ifdef SOLARIS_THREADS + GC_fresh_pages = (struct hblk **) + GC_scratch_alloc(MAX_FRESH_PAGES * sizeof (struct hblk *)); + if (GC_fresh_pages == 0) { + GC_err_printf0("No space for fresh pages\n"); + EXIT(); + } + BZERO(GC_fresh_pages, MAX_FRESH_PAGES * sizeof (struct hblk *)); +# endif +} + +/* Ignore write hints. They don't help us here. */ +/*ARGSUSED*/ +void GC_write_hint(h) +struct hblk *h; +{ +} + +#ifdef SOLARIS_THREADS +# define READ(fd,buf,nbytes) syscall(SYS_read, fd, buf, nbytes) +#else +# define READ(fd,buf,nbytes) read(fd, buf, nbytes) +#endif + +void GC_read_dirty() +{ + unsigned long ps, np; + int nmaps; + ptr_t vaddr; + struct prasmap * map; + char * bufp; + ptr_t current_addr, limit; + int i; +int dummy; + + BZERO(GC_grungy_pages, (sizeof GC_grungy_pages)); + + bufp = GC_proc_buf; + if (READ(GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { +# ifdef PRINTSTATS + GC_printf1("/proc read failed: GC_proc_buf_size = %lu\n", + GC_proc_buf_size); +# endif + { + /* Retry with larger buffer. */ + word new_size = 2 * GC_proc_buf_size; + char * new_buf = GC_scratch_alloc(new_size); + + if (new_buf != 0) { + GC_proc_buf = bufp = new_buf; + GC_proc_buf_size = new_size; + } + if (syscall(SYS_read, GC_proc_fd, bufp, GC_proc_buf_size) <= 0) { + WARN("Insufficient space for /proc read\n", 0); + /* Punt: */ + memset(GC_grungy_pages, 0xff, sizeof (page_hash_table)); + memset(GC_written_pages, 0xff, sizeof(page_hash_table)); +# ifdef SOLARIS_THREADS + BZERO(GC_fresh_pages, + MAX_FRESH_PAGES * sizeof (struct hblk *)); +# endif + return; + } + } + } + /* Copy dirty bits into GC_grungy_pages */ + nmaps = ((struct prpageheader *)bufp) -> pr_nmap; + /* printf( "nmaps = %d, PG_REFERENCED = %d, PG_MODIFIED = %d\n", + nmaps, PG_REFERENCED, PG_MODIFIED); */ + bufp = bufp + sizeof(struct prpageheader); + for (i = 0; i < nmaps; i++) { + map = (struct prasmap *)bufp; + vaddr = (ptr_t)(map -> pr_vaddr); + ps = map -> pr_pagesize; + np = map -> pr_npage; + /* printf("vaddr = 0x%X, ps = 0x%X, np = 0x%X\n", vaddr, ps, np); */ + limit = vaddr + ps * np; + bufp += sizeof (struct prasmap); + for (current_addr = vaddr; + current_addr < limit; current_addr += ps){ + if ((*bufp++) & PG_MODIFIED) { + register struct hblk * h = (struct hblk *) current_addr; + + while ((ptr_t)h < current_addr + ps) { + register word index = PHT_HASH(h); + + set_pht_entry_from_index(GC_grungy_pages, index); +# ifdef SOLARIS_THREADS + { + register int slot = FRESH_PAGE_SLOT(h); + + if (GC_fresh_pages[slot] == h) { + GC_fresh_pages[slot] = 0; + } + } +# endif + h++; + } + } + } + bufp += sizeof(long) - 1; + bufp = (char *)((unsigned long)bufp & ~(sizeof(long)-1)); + } + /* Update GC_written_pages. */ + GC_or_pages(GC_written_pages, GC_grungy_pages); +# ifdef SOLARIS_THREADS + /* Make sure that old stacks are considered completely clean */ + /* unless written again. */ + GC_old_stacks_are_fresh(); +# endif +} + +#undef READ + +GC_bool GC_page_was_dirty(h) +struct hblk *h; +{ + register word index = PHT_HASH(h); + register GC_bool result; + + result = get_pht_entry_from_index(GC_grungy_pages, index); +# ifdef SOLARIS_THREADS + if (result && PAGE_IS_FRESH(h)) result = FALSE; + /* This happens only if page was declared fresh since */ + /* the read_dirty call, e.g. because it's in an unused */ + /* thread stack. It's OK to treat it as clean, in */ + /* that case. And it's consistent with */ + /* GC_page_was_ever_dirty. */ +# endif + return(result); +} + +GC_bool GC_page_was_ever_dirty(h) +struct hblk *h; +{ + register word index = PHT_HASH(h); + register GC_bool result; + + result = get_pht_entry_from_index(GC_written_pages, index); +# ifdef SOLARIS_THREADS + if (result && PAGE_IS_FRESH(h)) result = FALSE; +# endif + return(result); +} + +/* Caller holds allocation lock. */ +void GC_is_fresh(h, n) +struct hblk *h; +word n; +{ + + register word index; + +# ifdef SOLARIS_THREADS + register word i; + + if (GC_fresh_pages != 0) { + for (i = 0; i < n; i++) { + ADD_FRESH_PAGE(h + i); + } + } +# endif +} + +# endif /* PROC_VDB */ + + +# ifdef PCR_VDB + +# include "vd/PCR_VD.h" + +# define NPAGES (32*1024) /* 128 MB */ + +PCR_VD_DB GC_grungy_bits[NPAGES]; + +ptr_t GC_vd_base; /* Address corresponding to GC_grungy_bits[0] */ + /* HBLKSIZE aligned. */ + +void GC_dirty_init() +{ + GC_dirty_maintained = TRUE; + /* For the time being, we assume the heap generally grows up */ + GC_vd_base = GC_heap_sects[0].hs_start; + if (GC_vd_base == 0) { + ABORT("Bad initial heap segment"); + } + if (PCR_VD_Start(HBLKSIZE, GC_vd_base, NPAGES*HBLKSIZE) + != PCR_ERes_okay) { + ABORT("dirty bit initialization failed"); + } +} + +void GC_read_dirty() +{ + /* lazily enable dirty bits on newly added heap sects */ + { + static int onhs = 0; + int nhs = GC_n_heap_sects; + for( ; onhs < nhs; onhs++ ) { + PCR_VD_WriteProtectEnable( + GC_heap_sects[onhs].hs_start, + GC_heap_sects[onhs].hs_bytes ); + } + } + + + if (PCR_VD_Clear(GC_vd_base, NPAGES*HBLKSIZE, GC_grungy_bits) + != PCR_ERes_okay) { + ABORT("dirty bit read failed"); + } +} + +GC_bool GC_page_was_dirty(h) +struct hblk *h; +{ + if((ptr_t)h < GC_vd_base || (ptr_t)h >= GC_vd_base + NPAGES*HBLKSIZE) { + return(TRUE); + } + return(GC_grungy_bits[h - (struct hblk *)GC_vd_base] & PCR_VD_DB_dirtyBit); +} + +/*ARGSUSED*/ +void GC_write_hint(h) +struct hblk *h; +{ + PCR_VD_WriteProtectDisable(h, HBLKSIZE); + PCR_VD_WriteProtectEnable(h, HBLKSIZE); +} + +# endif /* PCR_VDB */ + +/* + * Call stack save code for debugging. + * Should probably be in mach_dep.c, but that requires reorganization. + */ +#if defined(SPARC) && !defined(LINUX) +# if defined(SUNOS4) +# include <machine/frame.h> +# else +# if defined (DRSNX) +# include <sys/sparc/frame.h> +# else +# if defined(OPENBSD) +# include <frame.h> +# else +# include <sys/frame.h> +# endif +# endif +# endif +# if NARGS > 6 + --> We only know how to to get the first 6 arguments +# endif + +#ifdef SAVE_CALL_CHAIN +/* Fill in the pc and argument information for up to NFRAMES of my */ +/* callers. Ignore my frame and my callers frame. */ + +#ifdef OPENBSD +# define FR_SAVFP fr_fp +# define FR_SAVPC fr_pc +#else +# define FR_SAVFP fr_savfp +# define FR_SAVPC fr_savpc +#endif + +void GC_save_callers (info) +struct callinfo info[NFRAMES]; +{ + struct frame *frame; + struct frame *fp; + int nframes = 0; + word GC_save_regs_in_stack(); + + frame = (struct frame *) GC_save_regs_in_stack (); + + for (fp = frame -> FR_SAVFP; fp != 0 && nframes < NFRAMES; + fp = fp -> FR_SAVFP, nframes++) { + register int i; + + info[nframes].ci_pc = fp->FR_SAVPC; + for (i = 0; i < NARGS; i++) { + info[nframes].ci_arg[i] = ~(fp->fr_arg[i]); + } + } + if (nframes < NFRAMES) info[nframes].ci_pc = 0; +} + +#endif /* SAVE_CALL_CHAIN */ +#endif /* SPARC */ + + + diff --git a/gc/pc_excludes b/gc/pc_excludes new file mode 100644 index 0000000..52da431 --- /dev/null +++ b/gc/pc_excludes @@ -0,0 +1,15 @@ +solaris_threads.c +pcr_interface.c +real_malloc.c +mips_mach_dep.s +rs6000_mach_dep.s +alpha_mach_dep.s +sparc_mach_dep.s +PCR-Makefile +setjmp_t.c +callprocs +gc.man +pc_excludes +barrett_diagram +include/gc_c++.h +include/gc_inline.h
\ No newline at end of file diff --git a/gc/pcr_interface.c b/gc/pcr_interface.c new file mode 100644 index 0000000..4c95093 --- /dev/null +++ b/gc/pcr_interface.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 7, 1996 11:09 am PST */ +# include "gc_priv.h" + +# ifdef PCR +/* + * Note that POSIX PCR requires an ANSI C compiler. Hence we are allowed + * to make the same assumption here. + * We wrap all of the allocator functions to avoid questions of + * compatibility between the prototyped and nonprototyped versions of the f + */ +# include "config/PCR_StdTypes.h" +# include "mm/PCR_MM.h" +# include <errno.h> + +# define MY_MAGIC 17L +# define MY_DEBUGMAGIC 42L + +void * GC_AllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_malloc_atomic(size); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_malloc(size)); + } +} + +void * GC_DebugAllocProc(size_t size, PCR_Bool ptrFree, PCR_Bool clear ) +{ + if (ptrFree) { + void * result = (void *)GC_debug_malloc_atomic(size, __FILE__, + __LINE__); + if (clear && result != 0) BZERO(result, size); + return(result); + } else { + return((void *)GC_debug_malloc(size, __FILE__, __LINE__)); + } +} + +# define GC_ReallocProc GC_realloc +void * GC_DebugReallocProc(void * old_object, size_t new_size_in_bytes) +{ + return(GC_debug_realloc(old_object, new_size_in_bytes, __FILE__, __LINE__)); +} + +# define GC_FreeProc GC_free +# define GC_DebugFreeProc GC_debug_free + +typedef struct { + PCR_ERes (*ed_proc)(void *p, size_t size, PCR_Any data); + GC_bool ed_pointerfree; + PCR_ERes ed_fail_code; + PCR_Any ed_client_data; +} enumerate_data; + +void GC_enumerate_block(h, ed) +register struct hblk *h; +enumerate_data * ed; +{ + register hdr * hhdr; + register int sz; + word *p; + word * lim; + + hhdr = HDR(h); + sz = hhdr -> hb_sz; + if (sz >= 0 && ed -> ed_pointerfree + || sz <= 0 && !(ed -> ed_pointerfree)) return; + if (sz < 0) sz = -sz; + lim = (word *)(h+1) - sz; + p = (word *)h; + do { + if (PCR_ERes_IsErr(ed -> ed_fail_code)) return; + ed -> ed_fail_code = + (*(ed -> ed_proc))(p, WORDS_TO_BYTES(sz), ed -> ed_client_data); + p+= sz; + } while (p <= lim); +} + +struct PCR_MM_ProcsRep * GC_old_allocator = 0; + +PCR_ERes GC_EnumerateProc( + PCR_Bool ptrFree, + PCR_ERes (*proc)(void *p, size_t size, PCR_Any data), + PCR_Any data +) +{ + enumerate_data ed; + + ed.ed_proc = proc; + ed.ed_pointerfree = ptrFree; + ed.ed_fail_code = PCR_ERes_okay; + ed.ed_client_data = data; + GC_apply_to_all_blocks(GC_enumerate_block, &ed); + if (ed.ed_fail_code != PCR_ERes_okay) { + return(ed.ed_fail_code); + } else { + /* Also enumerate objects allocated by my predecessors */ + return((*(GC_old_allocator->mmp_enumerate))(ptrFree, proc, data)); + } +} + +void GC_DummyFreeProc(void *p) {} + +void GC_DummyShutdownProc(void) {} + +struct PCR_MM_ProcsRep GC_Rep = { + MY_MAGIC, + GC_AllocProc, + GC_ReallocProc, + GC_DummyFreeProc, /* mmp_free */ + GC_FreeProc, /* mmp_unsafeFree */ + GC_EnumerateProc, + GC_DummyShutdownProc /* mmp_shutdown */ +}; + +struct PCR_MM_ProcsRep GC_DebugRep = { + MY_DEBUGMAGIC, + GC_DebugAllocProc, + GC_DebugReallocProc, + GC_DummyFreeProc, /* mmp_free */ + GC_DebugFreeProc, /* mmp_unsafeFree */ + GC_EnumerateProc, + GC_DummyShutdownProc /* mmp_shutdown */ +}; + +GC_bool GC_use_debug = 0; + +void GC_pcr_install() +{ + PCR_MM_Install((GC_use_debug? &GC_DebugRep : &GC_Rep), &GC_old_allocator); +} + +PCR_ERes +PCR_GC_Setup(void) +{ + return PCR_ERes_okay; +} + +PCR_ERes +PCR_GC_Run(void) +{ + + if( !PCR_Base_TestPCRArg("-nogc") ) { + GC_quiet = ( PCR_Base_TestPCRArg("-gctrace") ? 0 : 1 ); + GC_use_debug = (GC_bool)PCR_Base_TestPCRArg("-debug_alloc"); + GC_init(); + if( !PCR_Base_TestPCRArg("-nogc_incremental") ) { + /* + * awful hack to test whether VD is implemented ... + */ + if( PCR_VD_Start( 0, NIL, 0) != PCR_ERes_FromErr(ENOSYS) ) { + GC_enable_incremental(); + } + } + } + return PCR_ERes_okay; +} + +# endif diff --git a/gc/ptr_chck.c b/gc/ptr_chck.c new file mode 100644 index 0000000..f3451ee --- /dev/null +++ b/gc/ptr_chck.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, September 19, 1995 1:26 pm PDT */ + +#include "gc_priv.h" +#include "gc_mark.h" + +#ifdef __STDC__ +void GC_default_same_obj_print_proc(GC_PTR p, GC_PTR q) +#else +void GC_default_same_obj_print_proc (p, q) +GC_PTR p, q; +#endif +{ + GC_err_printf2("0x%lx and 0x%lx are not in the same object\n", + (unsigned long)p, (unsigned long)q); + ABORT("GC_same_obj test failed"); +} + +void (*GC_same_obj_print_proc) GC_PROTO((GC_PTR, GC_PTR)) + = GC_default_same_obj_print_proc; + +/* Check that p and q point to the same object. Call */ +/* *GC_same_obj_print_proc if they don't. */ +/* Returns the first argument. (Return value may be hard */ +/* to use,due to typing issues. But if we had a suitable */ +/* preprocessor ...) */ +/* Succeeds if neither p nor q points to the heap. */ +/* We assume this is performance critical. (It shouldn't */ +/* be called by production code, but this can easily make */ +/* debugging intolerably slow.) */ +#ifdef __STDC__ + GC_PTR GC_same_obj(register void *p, register void *q) +#else + GC_PTR GC_same_obj(p, q) + register char *p, *q; +#endif +{ + register struct hblk *h; + register hdr *hhdr; + register ptr_t base, limit; + register word sz; + + if (!GC_is_initialized) GC_init(); + hhdr = HDR((word)p); + if (hhdr == 0) { + if (divHBLKSZ((word)p) != divHBLKSZ((word)q) + && HDR((word)q) != 0) { + goto fail; + } + return(p); + } + /* If it's a pointer to the middle of a large object, move it */ + /* to the beginning. */ + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = HBLKPTR(p) - (word)hhdr; + hhdr = HDR(h); + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } + limit = (ptr_t)((word *)h + HDR_WORDS + hhdr -> hb_sz); + if ((ptr_t)p >= limit || (ptr_t)q >= limit || (ptr_t)q < (ptr_t)h ) { + goto fail; + } + return(p); + } + sz = WORDS_TO_BYTES(hhdr -> hb_sz); + if (sz > WORDS_TO_BYTES(MAXOBJSZ)) { + base = (ptr_t)HBLKPTR(p); + limit = base + sz; + if ((ptr_t)p >= limit) { + goto fail; + } + } else { +# ifdef ALL_INTERIOR_POINTERS + register map_entry_type map_entry; + register int pdispl; + + pdispl = HBLKDISPL(p); + map_entry = MAP_ENTRY((hhdr -> hb_map), pdispl); + if (map_entry == OBJ_INVALID) { + goto fail; + } else { + base = (char *)((word)p & ~(WORDS_TO_BYTES(1) - 1)); + base -= WORDS_TO_BYTES(map_entry); + } +# else + register int offset = HBLKDISPL(p) - HDR_BYTES; + register word correction = offset % sz; + + if (HBLKPTR(p) != HBLKPTR(q)) { + /* The following computation otherwise fails in this case */ + goto fail; + } + base = (ptr_t)p - correction; +# endif + limit = base + sz; + } + /* [base, limit) delimits the object containing p, if any. */ + /* If p is not inside a valid object, then either q is */ + /* also outside any valid object, or it is outside */ + /* [base, limit). */ + if ((ptr_t)q >= limit || (ptr_t)q < base) { + goto fail; + } + return(p); +fail: + (*GC_same_obj_print_proc)((ptr_t)p, (ptr_t)q); + return(p); +} + +#ifdef __STDC__ +void GC_default_is_valid_displacement_print_proc (GC_PTR p) +#else +void GC_default_is_valid_displacement_print_proc (p) +GC_PTR p; +#endif +{ + GC_err_printf1("0x%lx does not point to valid object displacement\n", + (unsigned long)p); + ABORT("GC_is_valid_displacement test failed"); +} + +void (*GC_is_valid_displacement_print_proc) GC_PROTO((GC_PTR)) = + GC_default_is_valid_displacement_print_proc; + +/* Check that if p is a pointer to a heap page, then it points to */ +/* a valid displacement within a heap object. */ +/* Uninteresting with ALL_INTERIOR_POINTERS. */ +/* Always returns its argument. */ +/* Note that we don't lock, since nothing relevant about the header */ +/* should change while we have a valid object pointer to the block. */ +#ifdef __STDC__ + void * GC_is_valid_displacement(void *p) +#else + char *GC_is_valid_displacement(p) + char *p; +#endif +{ + register hdr *hhdr; + register word pdispl; + register struct hblk *h; + register map_entry_type map_entry; + register word sz; + + if (!GC_is_initialized) GC_init(); + hhdr = HDR((word)p); + if (hhdr == 0) return(p); + h = HBLKPTR(p); +# ifdef ALL_INTERIOR_POINTERS + while (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + h = FORWARDED_ADDR(h, hhdr); + hhdr = HDR(h); + } +# endif + if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) { + goto fail; + } + sz = WORDS_TO_BYTES(hhdr -> hb_sz); + pdispl = HBLKDISPL(p); + map_entry = MAP_ENTRY((hhdr -> hb_map), pdispl); + if (map_entry == OBJ_INVALID + || sz > MAXOBJSZ && (ptr_t)p >= (ptr_t)h + sz) { + goto fail; + } + return(p); +fail: + (*GC_is_valid_displacement_print_proc)((ptr_t)p); + return(p); +} + +#ifdef __STDC__ +void GC_default_is_visible_print_proc(GC_PTR p) +#else +void GC_default_is_visible_print_proc(p) +GC_PTR p; +#endif +{ + GC_err_printf1("0x%lx is not a GC visible pointer location\n", + (unsigned long)p); + ABORT("GC_is_visible test failed"); +} + +void (*GC_is_visible_print_proc) GC_PROTO((GC_PTR p)) = + GC_default_is_visible_print_proc; + +/* Could p be a stack address? */ +GC_bool GC_on_stack(p) +ptr_t p; +{ +# ifdef THREADS + return(TRUE); +# else + int dummy; +# ifdef STACK_GROWS_DOWN + if ((ptr_t)p >= (ptr_t)(&dummy) && (ptr_t)p < GC_stackbottom ) { + return(TRUE); + } +# else + if ((ptr_t)p <= (ptr_t)(&dummy) && (ptr_t)p > GC_stackbottom ) { + return(TRUE); + } +# endif + return(FALSE); +# endif +} + +/* Check that p is visible */ +/* to the collector as a possibly pointer containing location. */ +/* If it isn't invoke *GC_is_visible_print_proc. */ +/* Returns the argument in all cases. May erroneously succeed */ +/* in hard cases. (This is intended for debugging use with */ +/* untyped allocations. The idea is that it should be possible, though */ +/* slow, to add such a call to all indirect pointer stores.) */ +/* Currently useless for multithreaded worlds. */ +#ifdef __STDC__ + void * GC_is_visible(void *p) +#else + char *GC_is_visible(p) + char *p; +#endif +{ + register hdr *hhdr; + + if ((word)p & (ALIGNMENT - 1)) goto fail; + if (!GC_is_initialized) GC_init(); +# ifdef THREADS + hhdr = HDR((word)p); + if (hhdr != 0 && GC_base(p) == 0) { + goto fail; + } else { + /* May be inside thread stack. We can't do much. */ + return(p); + } +# else + /* Check stack first: */ + if (GC_on_stack(p)) return(p); + hhdr = HDR((word)p); + if (hhdr == 0) { + GC_bool result; + + if (GC_is_static_root(p)) return(p); + /* Else do it again correctly: */ +# if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(PCR)) \ + && !defined(SRC_M3) + DISABLE_SIGNALS(); + GC_register_dynamic_libraries(); + result = GC_is_static_root(p); + ENABLE_SIGNALS(); + if (result) return(p); +# endif + goto fail; + } else { + /* p points to the heap. */ + word descr; + ptr_t base = GC_base(p); /* Should be manually inlined? */ + + if (base == 0) goto fail; + if (HBLKPTR(base) != HBLKPTR(p)) hhdr = HDR((word)p); + descr = hhdr -> hb_descr; + retry: + switch(descr & DS_TAGS) { + case DS_LENGTH: + if ((word)((ptr_t)p - (ptr_t)base) > (word)descr) goto fail; + break; + case DS_BITMAP: + if ((ptr_t)p - (ptr_t)base + >= WORDS_TO_BYTES(BITMAP_BITS) + || ((word)p & (sizeof(word) - 1))) goto fail; + if (!((1 << (WORDSZ - ((ptr_t)p - (ptr_t)base) - 1)) + & descr)) goto fail; + break; + case DS_PROC: + /* We could try to decipher this partially. */ + /* For now we just punt. */ + break; + case DS_PER_OBJECT: + descr = *(word *)((ptr_t)base + (descr & ~DS_TAGS)); + goto retry; + } + return(p); + } +# endif +fail: + (*GC_is_visible_print_proc)((ptr_t)p); + return(p); +} + + +GC_PTR GC_pre_incr (p, how_much) +GC_PTR *p; +size_t how_much; +{ + GC_PTR initial = *p; + GC_PTR result = GC_same_obj((GC_PTR)((word)initial + how_much), initial); + +# ifndef ALL_INTERIOR_POINTERS + (void) GC_is_valid_displacement(result); +# endif + return (*p = result); +} + +GC_PTR GC_post_incr (p, how_much) +GC_PTR *p; +size_t how_much; +{ + GC_PTR initial = *p; + GC_PTR result = GC_same_obj((GC_PTR)((word)initial + how_much), initial); + +# ifndef ALL_INTERIOR_POINTERS + (void) GC_is_valid_displacement(result); +# endif + *p = result; + return(initial); +} diff --git a/gc/real_malloc.c b/gc/real_malloc.c new file mode 100644 index 0000000..dece9fd --- /dev/null +++ b/gc/real_malloc.c @@ -0,0 +1,36 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, May 19, 1994 2:04 pm PDT */ + + +# ifdef PCR +/* + * This definition should go in its own file that includes no other + * header files. Otherwise, we risk not getting the underlying system + * malloc. + */ +# define PCR_NO_RENAME +# include <stdlib.h> + +# ifdef __STDC__ + char * real_malloc(size_t size) +# else + char * real_malloc() + int size; +# endif +{ + return((char *)malloc(size)); +} +#endif /* PCR */ + diff --git a/gc/reclaim.c b/gc/reclaim.c new file mode 100644 index 0000000..3085946 --- /dev/null +++ b/gc/reclaim.c @@ -0,0 +1,739 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, February 15, 1996 2:41 pm PST */ + +#include <stdio.h> +#include "gc_priv.h" + +signed_word GC_mem_found = 0; + /* Number of words of memory reclaimed */ + +static void report_leak(p, sz) +ptr_t p; +word sz; +{ + if (HDR(p) -> hb_obj_kind == PTRFREE) { + GC_err_printf0("Leaked atomic object at "); + } else { + GC_err_printf0("Leaked composite object at "); + } + if (GC_debugging_started && GC_has_debug_info(p)) { + GC_print_obj(p); + } else { + GC_err_printf2("0x%lx (appr. size = %ld)\n", + (unsigned long)p, + (unsigned long)WORDS_TO_BYTES(sz)); + } +} + +# define FOUND_FREE(hblk, word_no) \ + { \ + report_leak((ptr_t)hblk + WORDS_TO_BYTES(word_no), \ + HDR(hblk) -> hb_sz); \ + } + +/* + * reclaim phase + * + */ + + +/* + * Test whether a block is completely empty, i.e. contains no marked + * objects. This does not require the block to be in physical + * memory. + */ + +GC_bool GC_block_empty(hhdr) +register hdr * hhdr; +{ + register word *p = (word *)(&(hhdr -> hb_marks[0])); + register word * plim = + (word *)(&(hhdr -> hb_marks[MARK_BITS_SZ])); + while (p < plim) { + if (*p++) return(FALSE); + } + return(TRUE); +} + +# ifdef GATHERSTATS +# define INCR_WORDS(sz) n_words_found += (sz) +# else +# define INCR_WORDS(sz) +# endif +/* + * Restore unmarked small objects in h of size sz to the object + * free list. Returns the new list. + * Clears unmarked objects. + */ +/*ARGSUSED*/ +ptr_t GC_reclaim_clear(hbp, hhdr, sz, list) +register struct hblk *hbp; /* ptr to current heap block */ +register hdr * hhdr; +register ptr_t list; +register word sz; +{ + register int word_no; + register word *p, *q, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + + p = (word *)(hbp->hb_body); + word_no = HDR_WORDS; + plim = (word *)((((word)hbp) + HBLKSIZE) + - WORDS_TO_BYTES(sz)); + + /* go through all words in block */ + while( p <= plim ) { + if( mark_bit_from_hdr(hhdr, word_no) ) { + p += sz; + } else { + INCR_WORDS(sz); + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + /* Clear object, advance p to next object in the process */ + q = p + sz; + p++; /* Skip link field */ + while (p < q) { + *p++ = 0; + } + } + word_no += sz; + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +} + +#ifndef SMALL_CONFIG + +/* + * A special case for 2 word composite objects (e.g. cons cells): + */ +/*ARGSUSED*/ +ptr_t GC_reclaim_clear2(hbp, hhdr, list) +register struct hblk *hbp; /* ptr to current heap block */ +hdr * hhdr; +register ptr_t list; +{ + register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + register word mark_word; + register int i; +# define DO_OBJ(start_displ) \ + if (!(mark_word & ((word)1 << start_displ))) { \ + p[start_displ] = (word)list; \ + list = (ptr_t)(p+start_displ); \ + p[start_displ+1] = 0; \ + INCR_WORDS(2); \ + } + + p = (word *)(hbp->hb_body); + plim = (word *)(((word)hbp) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + for (i = 0; i < WORDSZ; i += 8) { + DO_OBJ(0); + DO_OBJ(2); + DO_OBJ(4); + DO_OBJ(6); + p += 8; + mark_word >>= 8; + } + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +# undef DO_OBJ +} + +/* + * Another special case for 4 word composite objects: + */ +/*ARGSUSED*/ +ptr_t GC_reclaim_clear4(hbp, hhdr, list) +register struct hblk *hbp; /* ptr to current heap block */ +hdr * hhdr; +register ptr_t list; +{ + register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + register word mark_word; +# define DO_OBJ(start_displ) \ + if (!(mark_word & ((word)1 << start_displ))) { \ + p[start_displ] = (word)list; \ + list = (ptr_t)(p+start_displ); \ + p[start_displ+1] = 0; \ + p[start_displ+2] = 0; \ + p[start_displ+3] = 0; \ + INCR_WORDS(4); \ + } + + p = (word *)(hbp->hb_body); + plim = (word *)(((word)hbp) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + DO_OBJ(0); + DO_OBJ(4); + DO_OBJ(8); + DO_OBJ(12); + DO_OBJ(16); + DO_OBJ(20); + DO_OBJ(24); + DO_OBJ(28); +# if CPP_WORDSZ == 64 + DO_OBJ(32); + DO_OBJ(36); + DO_OBJ(40); + DO_OBJ(44); + DO_OBJ(48); + DO_OBJ(52); + DO_OBJ(56); + DO_OBJ(60); +# endif + p += WORDSZ; + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +# undef DO_OBJ +} + +#endif /* !SMALL_CONFIG */ + +/* The same thing, but don't clear objects: */ +/*ARGSUSED*/ +ptr_t GC_reclaim_uninit(hbp, hhdr, sz, list) +register struct hblk *hbp; /* ptr to current heap block */ +register hdr * hhdr; +register ptr_t list; +register word sz; +{ + register int word_no; + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + + p = (word *)(hbp->hb_body); + word_no = HDR_WORDS; + plim = (word *)((((word)hbp) + HBLKSIZE) + - WORDS_TO_BYTES(sz)); + + /* go through all words in block */ + while( p <= plim ) { + if( !mark_bit_from_hdr(hhdr, word_no) ) { + INCR_WORDS(sz); + /* object is available - put on list */ + obj_link(p) = list; + list = ((ptr_t)p); + } + p += sz; + word_no += sz; + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +} + +/* Don't really reclaim objects, just check for unmarked ones: */ +/*ARGSUSED*/ +void GC_reclaim_check(hbp, hhdr, sz) +register struct hblk *hbp; /* ptr to current heap block */ +register hdr * hhdr; +register word sz; +{ + register int word_no; + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + + p = (word *)(hbp->hb_body); + word_no = HDR_WORDS; + plim = (word *)((((word)hbp) + HBLKSIZE) + - WORDS_TO_BYTES(sz)); + + /* go through all words in block */ + while( p <= plim ) { + if( !mark_bit_from_hdr(hhdr, word_no) ) { + FOUND_FREE(hbp, word_no); + } + p += sz; + word_no += sz; + } +} + +#ifndef SMALL_CONFIG +/* + * Another special case for 2 word atomic objects: + */ +/*ARGSUSED*/ +ptr_t GC_reclaim_uninit2(hbp, hhdr, list) +register struct hblk *hbp; /* ptr to current heap block */ +hdr * hhdr; +register ptr_t list; +{ + register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + register word mark_word; + register int i; +# define DO_OBJ(start_displ) \ + if (!(mark_word & ((word)1 << start_displ))) { \ + p[start_displ] = (word)list; \ + list = (ptr_t)(p+start_displ); \ + INCR_WORDS(2); \ + } + + p = (word *)(hbp->hb_body); + plim = (word *)(((word)hbp) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + for (i = 0; i < WORDSZ; i += 8) { + DO_OBJ(0); + DO_OBJ(2); + DO_OBJ(4); + DO_OBJ(6); + p += 8; + mark_word >>= 8; + } + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +# undef DO_OBJ +} + +/* + * Another special case for 4 word atomic objects: + */ +/*ARGSUSED*/ +ptr_t GC_reclaim_uninit4(hbp, hhdr, list) +register struct hblk *hbp; /* ptr to current heap block */ +hdr * hhdr; +register ptr_t list; +{ + register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + register word mark_word; +# define DO_OBJ(start_displ) \ + if (!(mark_word & ((word)1 << start_displ))) { \ + p[start_displ] = (word)list; \ + list = (ptr_t)(p+start_displ); \ + INCR_WORDS(4); \ + } + + p = (word *)(hbp->hb_body); + plim = (word *)(((word)hbp) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + DO_OBJ(0); + DO_OBJ(4); + DO_OBJ(8); + DO_OBJ(12); + DO_OBJ(16); + DO_OBJ(20); + DO_OBJ(24); + DO_OBJ(28); +# if CPP_WORDSZ == 64 + DO_OBJ(32); + DO_OBJ(36); + DO_OBJ(40); + DO_OBJ(44); + DO_OBJ(48); + DO_OBJ(52); + DO_OBJ(56); + DO_OBJ(60); +# endif + p += WORDSZ; + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +# undef DO_OBJ +} + +/* Finally the one word case, which never requires any clearing: */ +/*ARGSUSED*/ +ptr_t GC_reclaim1(hbp, hhdr, list) +register struct hblk *hbp; /* ptr to current heap block */ +hdr * hhdr; +register ptr_t list; +{ + register word * mark_word_addr = &(hhdr->hb_marks[divWORDSZ(HDR_WORDS)]); + register word *p, *plim; +# ifdef GATHERSTATS + register int n_words_found = 0; +# endif + register word mark_word; + register int i; +# define DO_OBJ(start_displ) \ + if (!(mark_word & ((word)1 << start_displ))) { \ + p[start_displ] = (word)list; \ + list = (ptr_t)(p+start_displ); \ + INCR_WORDS(1); \ + } + + p = (word *)(hbp->hb_body); + plim = (word *)(((word)hbp) + HBLKSIZE); + + /* go through all words in block */ + while( p < plim ) { + mark_word = *mark_word_addr++; + for (i = 0; i < WORDSZ; i += 4) { + DO_OBJ(0); + DO_OBJ(1); + DO_OBJ(2); + DO_OBJ(3); + p += 4; + mark_word >>= 4; + } + } +# ifdef GATHERSTATS + GC_mem_found += n_words_found; +# endif + return(list); +# undef DO_OBJ +} + +#endif /* !SMALL_CONFIG */ + +/* + * Restore unmarked small objects in the block pointed to by hbp + * to the appropriate object free list. + * If entirely empty blocks are to be completely deallocated, then + * caller should perform that check. + */ +void GC_reclaim_small_nonempty_block(hbp, report_if_found) +register struct hblk *hbp; /* ptr to current heap block */ +int report_if_found; /* Abort if a reclaimable object is found */ +{ + hdr * hhdr; + register word sz; /* size of objects in current block */ + register struct obj_kind * ok; + register ptr_t * flh; + register int kind; + + hhdr = HDR(hbp); + sz = hhdr -> hb_sz; + hhdr -> hb_last_reclaimed = (unsigned short) GC_gc_no; + kind = hhdr -> hb_obj_kind; + ok = &GC_obj_kinds[kind]; + flh = &(ok -> ok_freelist[sz]); + GC_write_hint(hbp); + + if (report_if_found) { + GC_reclaim_check(hbp, hhdr, sz); + } else if (ok -> ok_init) { + switch(sz) { +# ifndef SMALL_CONFIG + case 1: + *flh = GC_reclaim1(hbp, hhdr, *flh); + break; + case 2: + *flh = GC_reclaim_clear2(hbp, hhdr, *flh); + break; + case 4: + *flh = GC_reclaim_clear4(hbp, hhdr, *flh); + break; +# endif + default: + *flh = GC_reclaim_clear(hbp, hhdr, sz, *flh); + break; + } + } else { + switch(sz) { +# ifndef SMALL_CONFIG + case 1: + *flh = GC_reclaim1(hbp, hhdr, *flh); + break; + case 2: + *flh = GC_reclaim_uninit2(hbp, hhdr, *flh); + break; + case 4: + *flh = GC_reclaim_uninit4(hbp, hhdr, *flh); + break; +# endif + default: + *flh = GC_reclaim_uninit(hbp, hhdr, sz, *flh); + break; + } + } + if (IS_UNCOLLECTABLE(kind)) GC_set_hdr_marks(hhdr); +} + +/* + * Restore an unmarked large object or an entirely empty blocks of small objects + * to the heap block free list. + * Otherwise enqueue the block for later processing + * by GC_reclaim_small_nonempty_block. + * If report_if_found is TRUE, then process any block immediately, and + * simply report free objects; do not actually reclaim them. + */ +void GC_reclaim_block(hbp, report_if_found) +register struct hblk *hbp; /* ptr to current heap block */ +word report_if_found; /* Abort if a reclaimable object is found */ +{ + register hdr * hhdr; + register word sz; /* size of objects in current block */ + register struct obj_kind * ok; + struct hblk ** rlh; + + hhdr = HDR(hbp); + sz = hhdr -> hb_sz; + ok = &GC_obj_kinds[hhdr -> hb_obj_kind]; + + if( sz > MAXOBJSZ ) { /* 1 big object */ + if( !mark_bit_from_hdr(hhdr, HDR_WORDS) ) { + if (report_if_found) { + FOUND_FREE(hbp, HDR_WORDS); + } else { +# ifdef GATHERSTATS + GC_mem_found += sz; +# endif + GC_freehblk(hbp); + } + } + } else { + GC_bool empty = GC_block_empty(hhdr); + if (report_if_found) { + GC_reclaim_small_nonempty_block(hbp, (int)report_if_found); + } else if (empty) { +# ifdef GATHERSTATS + GC_mem_found += BYTES_TO_WORDS(HBLKSIZE); +# endif + GC_freehblk(hbp); + } else { + /* group of smaller objects, enqueue the real work */ + rlh = &(ok -> ok_reclaim_list[sz]); + hhdr -> hb_next = *rlh; + *rlh = hbp; + } + } +} + +#if !defined(NO_DEBUGGING) +/* Routines to gather and print heap block info */ +/* intended for debugging. Otherwise should be called */ +/* with lock. */ +static size_t number_of_blocks; +static size_t total_bytes; + +/* Number of set bits in a word. Not performance critical. */ +static int set_bits(n) +word n; +{ + register word m = n; + register int result = 0; + + while (m > 0) { + if (m & 1) result++; + m >>= 1; + } + return(result); +} + +/* Return the number of set mark bits in the given header */ +int GC_n_set_marks(hhdr) +hdr * hhdr; +{ + register int result = 0; + register int i; + + for (i = 0; i < MARK_BITS_SZ; i++) { + result += set_bits(hhdr -> hb_marks[i]); + } + return(result); +} + +/*ARGSUSED*/ +void GC_print_block_descr(h, dummy) +struct hblk *h; +word dummy; +{ + register hdr * hhdr = HDR(h); + register size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz); + + GC_printf3("(%lu:%lu,%lu)", (unsigned long)(hhdr -> hb_obj_kind), + (unsigned long)bytes, + (unsigned long)(GC_n_set_marks(hhdr))); + bytes += HDR_BYTES + HBLKSIZE-1; + bytes &= ~(HBLKSIZE-1); + total_bytes += bytes; + number_of_blocks++; +} + +void GC_print_block_list() +{ + GC_printf0("(kind(0=ptrfree,1=normal,2=unc.,3=stubborn):size_in_bytes, #_marks_set)\n"); + number_of_blocks = 0; + total_bytes = 0; + GC_apply_to_all_blocks(GC_print_block_descr, (word)0); + GC_printf2("\nblocks = %lu, bytes = %lu\n", + (unsigned long)number_of_blocks, + (unsigned long)total_bytes); +} + +#endif /* NO_DEBUGGING */ + +/* + * Perform GC_reclaim_block on the entire heap, after first clearing + * small object free lists (if we are not just looking for leaks). + */ +void GC_start_reclaim(report_if_found) +int report_if_found; /* Abort if a GC_reclaimable object is found */ +{ + int kind; + + /* Clear reclaim- and free-lists */ + for (kind = 0; kind < GC_n_kinds; kind++) { + register ptr_t *fop; + register ptr_t *lim; + register struct hblk ** rlp; + register struct hblk ** rlim; + register struct hblk ** rlist = GC_obj_kinds[kind].ok_reclaim_list; + + if (rlist == 0) continue; /* This kind not used. */ + if (!report_if_found) { + lim = &(GC_obj_kinds[kind].ok_freelist[MAXOBJSZ+1]); + for( fop = GC_obj_kinds[kind].ok_freelist; fop < lim; fop++ ) { + *fop = 0; + } + } /* otherwise free list objects are marked, */ + /* and its safe to leave them */ + rlim = rlist + MAXOBJSZ+1; + for( rlp = rlist; rlp < rlim; rlp++ ) { + *rlp = 0; + } + } + +# ifdef PRINTBLOCKS + GC_printf0("GC_reclaim: current block sizes:\n"); + GC_print_block_list(); +# endif + + /* Go through all heap blocks (in hblklist) and reclaim unmarked objects */ + /* or enqueue the block for later processing. */ + GC_apply_to_all_blocks(GC_reclaim_block, (word)report_if_found); + +} + +/* + * Sweep blocks of the indicated object size and kind until either the + * appropriate free list is nonempty, or there are no more blocks to + * sweep. + */ +void GC_continue_reclaim(sz, kind) +word sz; /* words */ +int kind; +{ + register hdr * hhdr; + register struct hblk * hbp; + register struct obj_kind * ok = &(GC_obj_kinds[kind]); + struct hblk ** rlh = ok -> ok_reclaim_list; + ptr_t *flh = &(ok -> ok_freelist[sz]); + + if (rlh == 0) return; /* No blocks of this kind. */ + rlh += sz; + while ((hbp = *rlh) != 0) { + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + GC_reclaim_small_nonempty_block(hbp, FALSE); + if (*flh != 0) break; + } +} + +/* + * Reclaim all small blocks waiting to be reclaimed. + * Abort and return FALSE when/if (*stop_func)() returns TRUE. + * If this returns TRUE, then it's safe to restart the world + * with incorrectly cleared mark bits. + * If ignore_old is TRUE, then reclain only blocks that have been + * recently reclaimed, and discard the rest. + * Stop_func may be 0. + */ +GC_bool GC_reclaim_all(stop_func, ignore_old) +GC_stop_func stop_func; +GC_bool ignore_old; +{ + register word sz; + register int kind; + register hdr * hhdr; + register struct hblk * hbp; + register struct obj_kind * ok; + struct hblk ** rlp; + struct hblk ** rlh; +# ifdef PRINTTIMES + CLOCK_TYPE start_time; + CLOCK_TYPE done_time; + + GET_TIME(start_time); +# endif + + for (kind = 0; kind < GC_n_kinds; kind++) { + ok = &(GC_obj_kinds[kind]); + rlp = ok -> ok_reclaim_list; + if (rlp == 0) continue; + for (sz = 1; sz <= MAXOBJSZ; sz++) { + rlh = rlp + sz; + while ((hbp = *rlh) != 0) { + if (stop_func != (GC_stop_func)0 && (*stop_func)()) { + return(FALSE); + } + hhdr = HDR(hbp); + *rlh = hhdr -> hb_next; + if (!ignore_old || hhdr -> hb_last_reclaimed == GC_gc_no - 1) { + /* It's likely we'll need it this time, too */ + /* It's been touched recently, so this */ + /* shouldn't trigger paging. */ + GC_reclaim_small_nonempty_block(hbp, FALSE); + } + } + } + } +# ifdef PRINTTIMES + GET_TIME(done_time); + GC_printf1("Disposing of reclaim lists took %lu msecs\n", + MS_TIME_DIFF(done_time,start_time)); +# endif + return(TRUE); +} diff --git a/gc/rs6000_mach_dep.s b/gc/rs6000_mach_dep.s new file mode 100644 index 0000000..e0dbe80 --- /dev/null +++ b/gc/rs6000_mach_dep.s @@ -0,0 +1,105 @@ + .csect + .set r0,0 + .set r1,1 + .set r2,2 + .set r3,3 + .set r4,4 + .set r5,5 + .set r6,6 + .set r7,7 + .set r8,8 + .set r9,9 + .set r10,10 + .set r11,11 + .set r12,12 + .set r13,13 + .set r14,14 + .set r15,15 + .set r16,16 + .set r17,17 + .set r18,18 + .set r19,19 + .set r20,20 + .set r21,21 + .set r22,22 + .set r23,23 + .set r24,24 + .set r25,25 + .set r26,26 + .set r27,27 + .set r28,28 + .set r29,29 + .set r30,30 + .set r31,31 + + # Mark from machine registers that are saved by C compiler + .globl .GC_push_regs +.GC_push_regs: + .extern .GC_push_one + stu r1,-64(r1) # reserve stack frame + mflr r0 # save link register + st r0,0x48(r1) + oril r3,r2,0x0 # mark from r2 + bl .GC_push_one + cror 15,15,15 + oril r3,r13,0x0 # mark from r13-r31 + bl .GC_push_one + cror 15,15,15 + oril r3,r14,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r15,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r16,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r17,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r18,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r19,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r20,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r21,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r22,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r23,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r24,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r25,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r26,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r27,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r28,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r29,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r30,0x0 + bl .GC_push_one + cror 15,15,15 + oril r3,r31,0x0 + bl .GC_push_one + cror 15,15,15 + l r0,0x48(r1) + mtlr r0 + ai r1,r1,64 + br diff --git a/gc/setjmp_t.c b/gc/setjmp_t.c new file mode 100644 index 0000000..1c9253e --- /dev/null +++ b/gc/setjmp_t.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, September 21, 1995 5:39 pm PDT */ + +/* Check whether setjmp actually saves registers in jmp_buf. */ +/* If it doesn't, the generic mark_regs code won't work. */ +/* Compilers vary as to whether they will put x in a */ +/* (callee-save) register without -O. The code is */ +/* contrived such that any decent compiler should put x in */ +/* a callee-save register with -O. Thus it is is */ +/* recommended that this be run optimized. (If the machine */ +/* has no callee-save registers, then the generic code is */ +/* safe, but this will not be noticed by this piece of */ +/* code.) */ +#include <stdio.h> +#include <setjmp.h> +#include <string.h> +#include "gcconfig.h" + +#ifdef OS2 +/* GETPAGESIZE() is set to getpagesize() by default, but that */ +/* doesn't really exist, and the collector doesn't need it. */ +#define INCL_DOSFILEMGR +#define INCL_DOSMISC +#define INCL_DOSERRORS +#include <os2.h> + +int +getpagesize() +{ + ULONG result[1]; + + if (DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, + (void *)result, sizeof(ULONG)) != NO_ERROR) { + fprintf(stderr, "DosQuerySysInfo failed\n"); + result[0] = 4096; + } + return((int)(result[0])); +} +#endif + +struct {char a_a; char * a_b;} a; + +int * nested_sp() +{ + int dummy; + + return(&dummy); +} + +main() +{ + int dummy; + long ps = GETPAGESIZE(); + jmp_buf b; + register int x = (int)strlen("a"); /* 1, slightly disguised */ + static int y = 0; + + printf("This appears to be a %s running %s\n", MACH_TYPE, OS_TYPE); + if (nested_sp() < &dummy) { + printf("Stack appears to grow down, which is the default.\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)(&dummy) + ps) & ~(ps-1)); + } else { + printf("Stack appears to grow up.\n"); + printf("Define STACK_GROWS_UP in gc_private.h\n"); + printf("A good guess for STACKBOTTOM on this machine is 0x%lx.\n", + ((unsigned long)(&dummy) + ps) & ~(ps-1)); + } + printf("Note that this may vary between machines of ostensibly\n"); + printf("the same architecture (e.g. Sun 3/50s and 3/80s).\n"); + printf("On many machines the value is not fixed.\n"); + printf("A good guess for ALIGNMENT on this machine is %ld.\n", + (unsigned long)(&(a.a_b))-(unsigned long)(&a)); + + /* Encourage the compiler to keep x in a callee-save register */ + x = 2*x-1; + printf(""); + x = 2*x-1; + setjmp(b); + if (y == 1) { + if (x == 2) { + printf("Generic mark_regs code probably wont work\n"); +# if defined(SPARC) || defined(RS6000) || defined(VAX) || defined(MIPS) || defined(M68K) || defined(I386) || defined(NS32K) || defined(RT) + printf("Assembly code supplied\n"); +# else + printf("Need assembly code\n"); +# endif + } else if (x == 1) { + printf("Generic mark_regs code may work\n"); + } else { + printf("Very strange setjmp implementation\n"); + } + } + y++; + x = 2; + if (y == 1) longjmp(b,1); + return(0); +} + +int g(x) +int x; +{ + return(x); +} diff --git a/gc/solaris_pthreads.c b/gc/solaris_pthreads.c new file mode 100644 index 0000000..eb0ce67 --- /dev/null +++ b/gc/solaris_pthreads.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Support code for Solaris threads. Provides functionality we wish Sun + * had provided. Relies on some information we probably shouldn't rely on. + * Modified Peter C. for Solaris Posix Threads. + */ +/* Boehm, September 14, 1994 4:44 pm PDT */ +/* $Id: solaris_pthreads.c,v 1.1 2001/11/08 05:17:57 a-ito Exp $ */ + +# if defined(_SOLARIS_PTHREADS) +# include "gc_priv.h" +# include <pthread.h> +# include <thread.h> +# include <signal.h> +# include <fcntl.h> +# include <sys/types.h> +# include <sys/mman.h> +# include <sys/time.h> +# include <sys/resource.h> +# include <sys/stat.h> +# include <sys/syscall.h> +# include <sys/procfs.h> +# include <sys/lwp.h> +# include <sys/reg.h> +# define _CLASSIC_XOPEN_TYPES +# include <unistd.h> +# include <errno.h> +# include "solaris_threads.h" +# include <stdio.h> + +#undef pthread_join +#undef pthread_create + +pthread_cond_t GC_prom_join_cv; /* Broadcast when any thread terminates */ +pthread_cond_t GC_create_cv; /* Signalled when a new undetached */ + /* thread starts. */ + +extern GC_bool GC_multithreaded; + +/* We use the allocation lock to protect thread-related data structures. */ + +/* We stop the world using /proc primitives. This makes some */ +/* minimal assumptions about the threads implementation. */ +/* We don't play by the rules, since the rules make this */ +/* impossible (as of Solaris 2.3). Also note that as of */ +/* Solaris 2.3 the various thread and lwp suspension */ +/* primitives failed to stop threads by the time the request */ +/* is completed. */ + + + +int GC_pthread_join(pthread_t wait_for, void **status) +{ + return GC_thr_join((thread_t)wait_for, NULL, status); +} + + +int +GC_pthread_create(pthread_t *new_thread, + const pthread_attr_t *attr_in, + void * (*thread_execp)(void *), void *arg) +{ + int result; + GC_thread t; + pthread_t my_new_thread; + pthread_attr_t attr; + word my_flags = 0; + int flag; + void * stack; + size_t stack_size; + int n; + struct sched_param schedparam; + + (void)pthread_attr_getstacksize(attr_in, &stack_size); + (void)pthread_attr_getstackaddr(attr_in, &stack); + (void)pthread_attr_init(&attr); + + LOCK(); + if (!GC_thr_initialized) { + GC_thr_init(); + } + GC_multithreaded++; + + if (stack == 0) { + if (stack_size == 0) + stack_size = GC_min_stack_sz; + else + stack_size += thr_min_stack(); + + stack = (void *)GC_stack_alloc(&stack_size); + if (stack == 0) { + GC_multithreaded--; + UNLOCK(); + errno = ENOMEM; + return -1; + } + } else { + my_flags |= CLIENT_OWNS_STACK; + } + (void)pthread_attr_setstacksize(&attr, stack_size); + (void)pthread_attr_setstackaddr(&attr, stack); + (void)pthread_attr_getscope(attr_in, &n); + (void)pthread_attr_setscope(&attr, n); + (void)pthread_attr_getschedparam(attr_in, &schedparam); + (void)pthread_attr_setschedparam(&attr, &schedparam); + (void)pthread_attr_getschedpolicy(attr_in, &n); + (void)pthread_attr_setschedpolicy(&attr, n); + (void)pthread_attr_getinheritsched(attr_in, &n); + (void)pthread_attr_setinheritsched(&attr, n); + + (void)pthread_attr_getdetachstate(attr_in, &flag); + if (flag == PTHREAD_CREATE_DETACHED) { + my_flags |= DETACHED; + } + (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + /* + * thr_create can call malloc(), which if redirected will + * attempt to acquire the allocation lock. + * Unlock here to prevent deadlock. + */ + + +#if 0 +#ifdef I386 + UNLOCK(); +#endif +#endif + result = + pthread_create(&my_new_thread, &attr, thread_execp, arg); +#if 0 +#ifdef I386 + LOCK(); +#endif +#endif + if (result == 0) { + t = GC_new_thread(my_new_thread); + t -> flags = my_flags; + if (!(my_flags & DETACHED)) cond_init(&(t->join_cv), USYNC_THREAD, 0); + t -> stack = stack; + t -> stack_size = stack_size; + if (new_thread != 0) *new_thread = my_new_thread; + pthread_cond_signal(&GC_create_cv); + } else { + if (!(my_flags & CLIENT_OWNS_STACK)) { + GC_stack_free(stack, stack_size); + } + GC_multithreaded--; + } + UNLOCK(); + pthread_attr_destroy(&attr); + return(result); +} + +# else + +#ifndef LINT + int GC_no_sunOS_pthreads; +#endif + +# endif /* SOLARIS_THREADS */ + diff --git a/gc/solaris_threads.c b/gc/solaris_threads.c new file mode 100644 index 0000000..65b2c65 --- /dev/null +++ b/gc/solaris_threads.c @@ -0,0 +1,940 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* + * Support code for Solaris threads. Provides functionality we wish Sun + * had provided. Relies on some information we probably shouldn't rely on. + */ +/* Boehm, September 14, 1994 4:44 pm PDT */ + +# if defined(SOLARIS_THREADS) + +# include "gc_priv.h" +# include "solaris_threads.h" +# include <thread.h> +# include <synch.h> +# include <signal.h> +# include <fcntl.h> +# include <sys/types.h> +# include <sys/mman.h> +# include <sys/time.h> +# include <sys/resource.h> +# include <sys/stat.h> +# include <sys/syscall.h> +# include <sys/procfs.h> +# include <sys/lwp.h> +# include <sys/reg.h> +# define _CLASSIC_XOPEN_TYPES +# include <unistd.h> +# include <errno.h> + +/* + * This is the default size of the LWP arrays. If there are more LWPs + * than this when a stop-the-world GC happens, set_max_lwps will be + * called to cope. + * This must be higher than the number of LWPs at startup time. + * The threads library creates a thread early on, so the min. is 3 + */ +# define DEFAULT_MAX_LWPS 4 + +#undef thr_join +#undef thr_create +#undef thr_suspend +#undef thr_continue + +cond_t GC_prom_join_cv; /* Broadcast when any thread terminates */ +cond_t GC_create_cv; /* Signalled when a new undetached */ + /* thread starts. */ + + +#ifdef MMAP_STACKS +static int GC_zfd; +#endif /* MMAP_STACKS */ + +/* We use the allocation lock to protect thread-related data structures. */ + +/* We stop the world using /proc primitives. This makes some */ +/* minimal assumptions about the threads implementation. */ +/* We don't play by the rules, since the rules make this */ +/* impossible (as of Solaris 2.3). Also note that as of */ +/* Solaris 2.3 the various thread and lwp suspension */ +/* primitives failed to stop threads by the time the request */ +/* is completed. */ + + +static sigset_t old_mask; + +/* Sleep for n milliseconds, n < 1000 */ +void GC_msec_sleep(int n) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1000000*n; + if (syscall(SYS_nanosleep, &ts, 0) < 0) { + ABORT("nanosleep failed"); + } +} +/* Turn off preemption; gross but effective. */ +/* Caller has allocation lock. */ +/* Actually this is not needed under Solaris 2.3 and */ +/* 2.4, but hopefully that'll change. */ +void preempt_off() +{ + sigset_t set; + + (void)sigfillset(&set); + sigdelset(&set, SIGABRT); + syscall(SYS_sigprocmask, SIG_SETMASK, &set, &old_mask); +} + +void preempt_on() +{ + syscall(SYS_sigprocmask, SIG_SETMASK, &old_mask, NULL); +} + +int GC_main_proc_fd = -1; + + +struct lwp_cache_entry { + lwpid_t lc_id; + int lc_descr; /* /proc file descriptor. */ +} GC_lwp_cache_default[DEFAULT_MAX_LWPS]; + +static int max_lwps = DEFAULT_MAX_LWPS; +static struct lwp_cache_entry *GC_lwp_cache = GC_lwp_cache_default; + +static prgregset_t GC_lwp_registers_default[DEFAULT_MAX_LWPS]; +static prgregset_t *GC_lwp_registers = GC_lwp_registers_default; + +/* Return a file descriptor for the /proc entry corresponding */ +/* to the given lwp. The file descriptor may be stale if the */ +/* lwp exited and a new one was forked. */ +static int open_lwp(lwpid_t id) +{ + int result; + static int next_victim = 0; + register int i; + + for (i = 0; i < max_lwps; i++) { + if (GC_lwp_cache[i].lc_id == id) return(GC_lwp_cache[i].lc_descr); + } + result = syscall(SYS_ioctl, GC_main_proc_fd, PIOCOPENLWP, &id); + /* + * If PIOCOPENLWP fails, try closing fds in the cache until it succeeds. + */ + if (result < 0 && errno == EMFILE) { + for (i = 0; i < max_lwps; i++) { + if (GC_lwp_cache[i].lc_id != 0) { + (void)syscall(SYS_close, GC_lwp_cache[i].lc_descr); + result = syscall(SYS_ioctl, GC_main_proc_fd, PIOCOPENLWP, &id); + if (result >= 0 || (result < 0 && errno != EMFILE)) + break; + } + } + } + if (result < 0) { + if (errno == EMFILE) { + ABORT("Too many open files"); + } + return(-1) /* exited? */; + } + if (GC_lwp_cache[next_victim].lc_id != 0) + (void)syscall(SYS_close, GC_lwp_cache[next_victim].lc_descr); + GC_lwp_cache[next_victim].lc_id = id; + GC_lwp_cache[next_victim].lc_descr = result; + if (++next_victim >= max_lwps) + next_victim = 0; + return(result); +} + +static void uncache_lwp(lwpid_t id) +{ + register int i; + + for (i = 0; i < max_lwps; i++) { + if (GC_lwp_cache[i].lc_id == id) { + (void)syscall(SYS_close, GC_lwp_cache[id].lc_descr); + GC_lwp_cache[i].lc_id = 0; + break; + } + } +} + /* Sequence of current lwp ids */ +static lwpid_t GC_current_ids_default[DEFAULT_MAX_LWPS + 1]; +static lwpid_t *GC_current_ids = GC_current_ids_default; + + /* Temporary used below (can be big if large number of LWPs) */ +static lwpid_t last_ids_default[DEFAULT_MAX_LWPS + 1]; +static lwpid_t *last_ids = last_ids_default; + + +#define ROUNDUP(n) WORDS_TO_BYTES(ROUNDED_UP_WORDS(n)) + +static void set_max_lwps(GC_word n) +{ + char *mem; + char *oldmem; + int required_bytes = ROUNDUP(n * sizeof(struct lwp_cache_entry)) + + ROUNDUP(n * sizeof(prgregset_t)) + + ROUNDUP((n + 1) * sizeof(lwpid_t)) + + ROUNDUP((n + 1) * sizeof(lwpid_t)); + + GC_expand_hp_inner(divHBLKSZ((word)required_bytes)); + oldmem = mem = GC_scratch_alloc(required_bytes); + if (0 == mem) ABORT("No space for lwp data structures"); + + /* + * We can either flush the old lwp cache or copy it over. Do the latter. + */ + memcpy(mem, GC_lwp_cache, max_lwps * sizeof(struct lwp_cache_entry)); + GC_lwp_cache = (struct lwp_cache_entry*)mem; + mem += ROUNDUP(n * sizeof(struct lwp_cache_entry)); + + BZERO(GC_lwp_registers, max_lwps * sizeof(GC_lwp_registers[0])); + GC_lwp_registers = (prgregset_t *)mem; + mem += ROUNDUP(n * sizeof(prgregset_t)); + + + GC_current_ids = (lwpid_t *)mem; + mem += ROUNDUP((n + 1) * sizeof(lwpid_t)); + + last_ids = (lwpid_t *)mem; + mem += ROUNDUP((n + 1)* sizeof(lwpid_t)); + + if (mem > oldmem + required_bytes) + ABORT("set_max_lwps buffer overflow"); + + max_lwps = n; +} + + +/* Stop all lwps in process. Assumes preemption is off. */ +/* Caller has allocation lock (and any other locks he may */ +/* need). */ +static void stop_all_lwps() +{ + int lwp_fd; + char buf[30]; + prstatus_t status; + register int i; + GC_bool changed; + lwpid_t me = _lwp_self(); + + if (GC_main_proc_fd == -1) { + sprintf(buf, "/proc/%d", getpid()); + GC_main_proc_fd = syscall(SYS_open, buf, O_RDONLY); + if (GC_main_proc_fd < 0) { + if (errno == EMFILE) + ABORT("/proc open failed: too many open files"); + GC_printf1("/proc open failed: errno %d", errno); + abort(); + } + } + BZERO(GC_lwp_registers, sizeof (prgregset_t) * max_lwps); + for (i = 0; i < max_lwps; i++) + last_ids[i] = 0; + for (;;) { + if (syscall(SYS_ioctl, GC_main_proc_fd, PIOCSTATUS, &status) < 0) + ABORT("Main PIOCSTATUS failed"); + if (status.pr_nlwp < 1) + ABORT("Invalid number of lwps returned by PIOCSTATUS"); + if (status.pr_nlwp >= max_lwps) { + set_max_lwps(status.pr_nlwp*2 + 10); + /* + * The data in the old GC_current_ids and + * GC_lwp_registers has been trashed. Cleaning out last_ids + * will make sure every LWP gets re-examined. + */ + for (i = 0; i < max_lwps; i++) + last_ids[i] = 0; + continue; + } + if (syscall(SYS_ioctl, GC_main_proc_fd, PIOCLWPIDS, GC_current_ids) < 0) + ABORT("PIOCLWPIDS failed"); + changed = FALSE; + for (i = 0; GC_current_ids[i] != 0 && i < max_lwps; i++) { + if (GC_current_ids[i] != last_ids[i]) { + changed = TRUE; + if (GC_current_ids[i] != me) { + /* PIOCSTOP doesn't work without a writable */ + /* descriptor. And that makes the process */ + /* undebuggable. */ + if (_lwp_suspend(GC_current_ids[i]) < 0) { + /* Could happen if the lwp exited */ + uncache_lwp(GC_current_ids[i]); + GC_current_ids[i] = me; /* ignore */ + } + } + } + } + /* + * In the unlikely event something does a fork between the + * PIOCSTATUS and the PIOCLWPIDS. + */ + if (i >= max_lwps) + continue; + /* All lwps in GC_current_ids != me have been suspended. Note */ + /* that _lwp_suspend is idempotent. */ + for (i = 0; GC_current_ids[i] != 0; i++) { + if (GC_current_ids[i] != last_ids[i]) { + if (GC_current_ids[i] != me) { + lwp_fd = open_lwp(GC_current_ids[i]); + if (lwp_fd == -1) + { + GC_current_ids[i] = me; + continue; + } + /* LWP should be stopped. Empirically it sometimes */ + /* isn't, and more frequently the PR_STOPPED flag */ + /* is not set. Wait for PR_STOPPED. */ + if (syscall(SYS_ioctl, lwp_fd, + PIOCSTATUS, &status) < 0) { + /* Possible if the descriptor was stale, or */ + /* we encountered the 2.3 _lwp_suspend bug. */ + uncache_lwp(GC_current_ids[i]); + GC_current_ids[i] = me; /* handle next time. */ + } else { + while (!(status.pr_flags & PR_STOPPED)) { + GC_msec_sleep(1); + if (syscall(SYS_ioctl, lwp_fd, + PIOCSTATUS, &status) < 0) { + ABORT("Repeated PIOCSTATUS failed"); + } + if (status.pr_flags & PR_STOPPED) break; + + GC_msec_sleep(20); + if (syscall(SYS_ioctl, lwp_fd, + PIOCSTATUS, &status) < 0) { + ABORT("Repeated PIOCSTATUS failed"); + } + } + if (status.pr_who != GC_current_ids[i]) { + /* can happen if thread was on death row */ + uncache_lwp(GC_current_ids[i]); + GC_current_ids[i] = me; /* handle next time. */ + continue; + } + /* Save registers where collector can */ + /* find them. */ + BCOPY(status.pr_reg, GC_lwp_registers[i], + sizeof (prgregset_t)); + } + } + } + } + if (!changed) break; + for (i = 0; i < max_lwps; i++) last_ids[i] = GC_current_ids[i]; + } +} + +/* Restart all lwps in process. Assumes preemption is off. */ +static void restart_all_lwps() +{ + int lwp_fd; + register int i; + GC_bool changed; + lwpid_t me = _lwp_self(); +# define PARANOID + + for (i = 0; GC_current_ids[i] != 0; i++) { +# ifdef PARANOID + if (GC_current_ids[i] != me) { + int lwp_fd = open_lwp(GC_current_ids[i]); + prstatus_t status; + + if (lwp_fd < 0) ABORT("open_lwp failed"); + if (syscall(SYS_ioctl, lwp_fd, + PIOCSTATUS, &status) < 0) { + ABORT("PIOCSTATUS failed in restart_all_lwps"); + } + if (memcmp(status.pr_reg, GC_lwp_registers[i], + sizeof (prgregset_t)) != 0) { + int j; + + for(j = 0; j < NGREG; j++) + { + GC_printf3("%i: %x -> %x\n", j, + GC_lwp_registers[i][j], + status.pr_reg[j]); + } + ABORT("Register contents changed"); + } + if (!status.pr_flags & PR_STOPPED) { + ABORT("lwp no longer stopped"); + } +#ifdef SPARC + { + gwindows_t windows; + if (syscall(SYS_ioctl, lwp_fd, + PIOCGWIN, &windows) < 0) { + ABORT("PIOCSTATUS failed in restart_all_lwps"); + } + if (windows.wbcnt > 0) ABORT("unsaved register windows"); + } +#endif + } +# endif /* PARANOID */ + if (GC_current_ids[i] == me) continue; + if (_lwp_continue(GC_current_ids[i]) < 0) { + ABORT("Failed to restart lwp"); + } + } + if (i >= max_lwps) ABORT("Too many lwps"); +} + +GC_bool GC_multithreaded = 0; + +void GC_stop_world() +{ + preempt_off(); + if (GC_multithreaded) + stop_all_lwps(); +} + +void GC_start_world() +{ + if (GC_multithreaded) + restart_all_lwps(); + preempt_on(); +} + +void GC_thr_init(void); + +GC_bool GC_thr_initialized = FALSE; + +size_t GC_min_stack_sz; + +size_t GC_page_sz; + +/* + * stack_head is stored at the top of free stacks + */ +struct stack_head { + struct stack_head *next; + ptr_t base; + thread_t owner; +}; + +# define N_FREE_LISTS 25 +struct stack_head *GC_stack_free_lists[N_FREE_LISTS] = { 0 }; + /* GC_stack_free_lists[i] is free list for stacks of */ + /* size GC_min_stack_sz*2**i. */ + /* Free lists are linked through stack_head stored */ /* at top of stack. */ + +/* Return a stack of size at least *stack_size. *stack_size is */ +/* replaced by the actual stack size. */ +/* Caller holds allocation lock. */ +ptr_t GC_stack_alloc(size_t * stack_size) +{ + register size_t requested_sz = *stack_size; + register size_t search_sz = GC_min_stack_sz; + register int index = 0; /* = log2(search_sz/GC_min_stack_sz) */ + register ptr_t base; + register struct stack_head *result; + + while (search_sz < requested_sz) { + search_sz *= 2; + index++; + } + if ((result = GC_stack_free_lists[index]) == 0 + && (result = GC_stack_free_lists[index+1]) != 0) { + /* Try next size up. */ + search_sz *= 2; index++; + } + if (result != 0) { + base = GC_stack_free_lists[index]->base; + GC_stack_free_lists[index] = GC_stack_free_lists[index]->next; + } else { +#ifdef MMAP_STACKS + base = (ptr_t)mmap(0, search_sz + GC_page_sz, + PROT_READ|PROT_WRITE, MAP_PRIVATE |MAP_NORESERVE, + GC_zfd, 0); + if (base == (ptr_t)-1) + { + *stack_size = 0; + return NULL; + } + + mprotect(base, GC_page_sz, PROT_NONE); + /* Should this use divHBLKSZ(search_sz + GC_page_sz) ? -- cf */ + GC_is_fresh((struct hblk *)base, divHBLKSZ(search_sz)); + base += GC_page_sz; + +#else + base = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz); + if (base == NULL) + { + *stack_size = 0; + return NULL; + } + + base = (ptr_t)(((word)base + GC_page_sz) & ~(GC_page_sz - 1)); + /* Protect hottest page to detect overflow. */ +# ifdef SOLARIS23_MPROTECT_BUG_FIXED + mprotect(base, GC_page_sz, PROT_NONE); +# endif + GC_is_fresh((struct hblk *)base, divHBLKSZ(search_sz)); + + base += GC_page_sz; +#endif + } + *stack_size = search_sz; + return(base); +} + +/* Caller holds allocationlock. */ +void GC_stack_free(ptr_t stack, size_t size) +{ + register int index = 0; + register size_t search_sz = GC_min_stack_sz; + register struct stack_head *head; + +#ifdef MMAP_STACKS + /* Zero pointers */ + mmap(stack, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_NORESERVE|MAP_FIXED, + GC_zfd, 0); +#endif + while (search_sz < size) { + search_sz *= 2; + index++; + } + if (search_sz != size) ABORT("Bad stack size"); + + head = (struct stack_head *)(stack + search_sz - sizeof(struct stack_head)); + head->next = GC_stack_free_lists[index]; + head->base = stack; + GC_stack_free_lists[index] = head; +} + +void GC_my_stack_limits(); + +/* Notify virtual dirty bit implementation that known empty parts of */ +/* stacks do not contain useful data. */ +/* Caller holds allocation lock. */ +void GC_old_stacks_are_fresh() +{ +/* No point in doing this for MMAP stacks - and pointers are zero'd out */ +/* by the mmap in GC_stack_free */ +#ifndef MMAP_STACKS + register int i; + register struct stack_head *s; + register ptr_t p; + register size_t sz; + register struct hblk * h; + int dummy; + + for (i = 0, sz= GC_min_stack_sz; i < N_FREE_LISTS; + i++, sz *= 2) { + for (s = GC_stack_free_lists[i]; s != 0; s = s->next) { + p = s->base; + h = (struct hblk *)(((word)p + HBLKSIZE-1) & ~(HBLKSIZE-1)); + if ((ptr_t)h == p) { + GC_is_fresh((struct hblk *)p, divHBLKSZ(sz)); + } else { + GC_is_fresh((struct hblk *)p, divHBLKSZ(sz) - 1); + BZERO(p, (ptr_t)h - p); + } + } + } +#endif /* MMAP_STACKS */ + GC_my_stack_limits(); +} + +/* The set of all known threads. We intercept thread creation and */ +/* joins. We never actually create detached threads. We allocate all */ +/* new thread stacks ourselves. These allow us to maintain this */ +/* data structure. */ + +# define THREAD_TABLE_SZ 128 /* Must be power of 2 */ +volatile GC_thread GC_threads[THREAD_TABLE_SZ]; + +/* Add a thread to GC_threads. We assume it wasn't already there. */ +/* Caller holds allocation lock. */ +GC_thread GC_new_thread(thread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + GC_thread result; + static struct GC_Thread_Rep first_thread; + static GC_bool first_thread_used = FALSE; + + if (!first_thread_used) { + result = &first_thread; + first_thread_used = TRUE; + /* Dont acquire allocation lock, since we may already hold it. */ + } else { + result = (struct GC_Thread_Rep *) + GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL); + } + if (result == 0) return(0); + result -> id = id; + result -> next = GC_threads[hv]; + GC_threads[hv] = result; + /* result -> finished = 0; */ + (void) cond_init(&(result->join_cv), USYNC_THREAD, 0); + return(result); +} + +/* Delete a thread from GC_threads. We assume it is there. */ +/* (The code intentionally traps if it wasn't.) */ +/* Caller holds allocation lock. */ +void GC_delete_thread(thread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + register GC_thread prev = 0; + + while (p -> id != id) { + prev = p; + p = p -> next; + } + if (prev == 0) { + GC_threads[hv] = p -> next; + } else { + prev -> next = p -> next; + } +} + +/* Return the GC_thread correpsonding to a given thread_t. */ +/* Returns 0 if it's not there. */ +/* Caller holds allocation lock. */ +GC_thread GC_lookup_thread(thread_t id) +{ + int hv = ((word)id) % THREAD_TABLE_SZ; + register GC_thread p = GC_threads[hv]; + + while (p != 0 && p -> id != id) p = p -> next; + return(p); +} + +# define MAX_ORIG_STACK_SIZE (8 * 1024 * 1024) + +word GC_get_orig_stack_size() { + struct rlimit rl; + static int warned = 0; + int result; + + if (getrlimit(RLIMIT_STACK, &rl) != 0) ABORT("getrlimit failed"); + result = (word)rl.rlim_cur & ~(HBLKSIZE-1); + if (result > MAX_ORIG_STACK_SIZE) { + if (!warned) { + WARN("Large stack limit(%ld): only scanning 8 MB", result); + warned = 1; + } + result = MAX_ORIG_STACK_SIZE; + } + return result; +} + +/* Notify dirty bit implementation of unused parts of my stack. */ +/* Caller holds allocation lock. */ +void GC_my_stack_limits() +{ + int dummy; + register ptr_t hottest = (ptr_t)((word)(&dummy) & ~(HBLKSIZE-1)); + register GC_thread me = GC_lookup_thread(thr_self()); + register size_t stack_size = me -> stack_size; + register ptr_t stack; + + if (stack_size == 0) { + /* original thread */ + /* Empirically, what should be the stack page with lowest */ + /* address is actually inaccessible. */ + stack_size = GC_get_orig_stack_size() - GC_page_sz; + stack = GC_stackbottom - stack_size + GC_page_sz; + } else { + stack = me -> stack; + } + if (stack > hottest || stack + stack_size < hottest) { + ABORT("sp out of bounds"); + } + GC_is_fresh((struct hblk *)stack, divHBLKSZ(hottest - stack)); +} + + +/* We hold allocation lock. We assume the world is stopped. */ +void GC_push_all_stacks() +{ + register int i; + register GC_thread p; + register ptr_t sp = GC_approx_sp(); + register ptr_t bottom, top; + struct rlimit rl; + +# define PUSH(bottom,top) \ + if (GC_dirty_maintained) { \ + GC_push_dirty((bottom), (top), GC_page_was_ever_dirty, \ + GC_push_all_stack); \ + } else { \ + GC_push_all_stack((bottom), (top)); \ + } + GC_push_all_stack((ptr_t)GC_lwp_registers, + (ptr_t)GC_lwp_registers + + max_lwps * sizeof(GC_lwp_registers[0])); + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> stack_size != 0) { + bottom = p -> stack; + top = p -> stack + p -> stack_size; + } else { + /* The original stack. */ + bottom = GC_stackbottom - GC_get_orig_stack_size() + GC_page_sz; + top = GC_stackbottom; + } + if ((word)sp > (word)bottom && (word)sp < (word)top) bottom = sp; + PUSH(bottom, top); + } + } +} + + +int GC_is_thread_stack(ptr_t addr) +{ + register int i; + register GC_thread p; + register ptr_t bottom, top; + struct rlimit rl; + + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (p = GC_threads[i]; p != 0; p = p -> next) { + if (p -> stack_size != 0) { + if (p -> stack <= addr && + addr < p -> stack + p -> stack_size) + return 1; + } + } + } +} + +/* The only thread that ever really performs a thr_join. */ +void * GC_thr_daemon(void * dummy) +{ + void *status; + thread_t departed; + register GC_thread t; + register int i; + register int result; + + for(;;) { + start: + result = thr_join((thread_t)0, &departed, &status); + LOCK(); + if (result != 0) { + /* No more threads; wait for create. */ + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> next) { + if (!(t -> flags & (DETACHED | FINISHED))) { + UNLOCK(); + goto start; /* Thread started just before we */ + /* acquired the lock. */ + } + } + } + cond_wait(&GC_create_cv, &GC_allocate_ml); + UNLOCK(); + } else { + t = GC_lookup_thread(departed); + GC_multithreaded--; + if (!(t -> flags & CLIENT_OWNS_STACK)) { + GC_stack_free(t -> stack, t -> stack_size); + } + if (t -> flags & DETACHED) { + GC_delete_thread(departed); + } else { + t -> status = status; + t -> flags |= FINISHED; + cond_signal(&(t -> join_cv)); + cond_broadcast(&GC_prom_join_cv); + } + UNLOCK(); + } + } +} + +/* We hold the allocation lock, or caller ensures that 2 instances */ +/* cannot be invoked concurrently. */ +void GC_thr_init(void) +{ + GC_thread t; + thread_t tid; + + if (GC_thr_initialized) + return; + GC_thr_initialized = TRUE; + GC_min_stack_sz = ((thr_min_stack() + 32*1024 + HBLKSIZE-1) + & ~(HBLKSIZE - 1)); + GC_page_sz = sysconf(_SC_PAGESIZE); +#ifdef MMAP_STACKS + GC_zfd = open("/dev/zero", O_RDONLY); + if (GC_zfd == -1) + ABORT("Can't open /dev/zero"); +#endif /* MMAP_STACKS */ + cond_init(&GC_prom_join_cv, USYNC_THREAD, 0); + cond_init(&GC_create_cv, USYNC_THREAD, 0); + /* Add the initial thread, so we can stop it. */ + t = GC_new_thread(thr_self()); + t -> stack_size = 0; + t -> flags = DETACHED | CLIENT_OWNS_STACK; + if (thr_create(0 /* stack */, 0 /* stack_size */, GC_thr_daemon, + 0 /* arg */, THR_DETACHED | THR_DAEMON, + &tid /* thread_id */) != 0) { + ABORT("Cant fork daemon"); + } + thr_setprio(tid, 126); +} + +/* We acquire the allocation lock to prevent races with */ +/* stopping/starting world. */ +/* This is no more correct than the underlying Solaris 2.X */ +/* implementation. Under 2.3 THIS IS BROKEN. */ +int GC_thr_suspend(thread_t target_thread) +{ + GC_thread t; + int result; + + LOCK(); + result = thr_suspend(target_thread); + if (result == 0) { + t = GC_lookup_thread(target_thread); + if (t == 0) ABORT("thread unknown to GC"); + t -> flags |= SUSPENDED; + } + UNLOCK(); + return(result); +} + +int GC_thr_continue(thread_t target_thread) +{ + GC_thread t; + int result; + + LOCK(); + result = thr_continue(target_thread); + if (result == 0) { + t = GC_lookup_thread(target_thread); + if (t == 0) ABORT("thread unknown to GC"); + t -> flags &= ~SUSPENDED; + } + UNLOCK(); + return(result); +} + +int GC_thr_join(thread_t wait_for, thread_t *departed, void **status) +{ + register GC_thread t; + int result = 0; + + LOCK(); + if (wait_for == 0) { + register int i; + register GC_bool thread_exists; + + for (;;) { + thread_exists = FALSE; + for (i = 0; i < THREAD_TABLE_SZ; i++) { + for (t = GC_threads[i]; t != 0; t = t -> next) { + if (!(t -> flags & DETACHED)) { + if (t -> flags & FINISHED) { + goto found; + } + thread_exists = TRUE; + } + } + } + if (!thread_exists) { + result = ESRCH; + goto out; + } + cond_wait(&GC_prom_join_cv, &GC_allocate_ml); + } + } else { + t = GC_lookup_thread(wait_for); + if (t == 0 || t -> flags & DETACHED) { + result = ESRCH; + goto out; + } + if (wait_for == thr_self()) { + result = EDEADLK; + goto out; + } + while (!(t -> flags & FINISHED)) { + cond_wait(&(t -> join_cv), &GC_allocate_ml); + } + + } + found: + if (status) *status = t -> status; + if (departed) *departed = t -> id; + cond_destroy(&(t -> join_cv)); + GC_delete_thread(t -> id); + out: + UNLOCK(); + return(result); +} + + +int +GC_thr_create(void *stack_base, size_t stack_size, + void *(*start_routine)(void *), void *arg, long flags, + thread_t *new_thread) +{ + int result; + GC_thread t; + thread_t my_new_thread; + word my_flags = 0; + void * stack = stack_base; + + LOCK(); + if (!GC_thr_initialized) + { + GC_thr_init(); + } + GC_multithreaded++; + if (stack == 0) { + if (stack_size == 0) stack_size = GC_min_stack_sz; + stack = (void *)GC_stack_alloc(&stack_size); + if (stack == 0) { + GC_multithreaded--; + UNLOCK(); + return(ENOMEM); + } + } else { + my_flags |= CLIENT_OWNS_STACK; + } + if (flags & THR_DETACHED) my_flags |= DETACHED; + if (flags & THR_SUSPENDED) my_flags |= SUSPENDED; + result = thr_create(stack, stack_size, start_routine, + arg, flags & ~THR_DETACHED, &my_new_thread); + if (result == 0) { + t = GC_new_thread(my_new_thread); + t -> flags = my_flags; + if (!(my_flags & DETACHED)) cond_init(&(t -> join_cv), USYNC_THREAD, 0); + t -> stack = stack; + t -> stack_size = stack_size; + if (new_thread != 0) *new_thread = my_new_thread; + cond_signal(&GC_create_cv); + } else { + GC_multithreaded--; + if (!(my_flags & CLIENT_OWNS_STACK)) { + GC_stack_free(stack, stack_size); + } + } + UNLOCK(); + return(result); +} + +# else /* SOLARIS_THREADS */ + +#ifndef LINT + int GC_no_sunOS_threads; +#endif +#endif diff --git a/gc/solaris_threads.h b/gc/solaris_threads.h new file mode 100644 index 0000000..b2cdb36 --- /dev/null +++ b/gc/solaris_threads.h @@ -0,0 +1,34 @@ +#ifdef SOLARIS_THREADS + +/* The set of all known threads. We intercept thread creation and */ +/* joins. We never actually create detached threads. We allocate all */ +/* new thread stacks ourselves. These allow us to maintain this */ +/* data structure. */ +/* Protected by GC_thr_lock. */ +/* Some of this should be declared volatile, but that's incosnsistent */ +/* with some library routine declarations. In particular, the */ +/* definition of cond_t doesn't mention volatile! */ + typedef struct GC_Thread_Rep { + struct GC_Thread_Rep * next; + thread_t id; + word flags; +# define FINISHED 1 /* Thread has exited. */ +# define DETACHED 2 /* Thread is intended to be detached. */ +# define CLIENT_OWNS_STACK 4 + /* Stack was supplied by client. */ +# define SUSPENDED 8 /* Currently suspended. */ + ptr_t stack; + size_t stack_size; + cond_t join_cv; + void * status; + } * GC_thread; + extern GC_thread GC_new_thread(thread_t id); + + extern GC_bool GC_thr_initialized; + extern volatile GC_thread GC_threads[]; + extern size_t GC_min_stack_sz; + extern size_t GC_page_sz; + extern void GC_thr_init(void); + +# endif /* SOLARIS_THREADS */ + diff --git a/gc/sparc_mach_dep.s b/gc/sparc_mach_dep.s new file mode 100644 index 0000000..9831c6c --- /dev/null +++ b/gc/sparc_mach_dep.s @@ -0,0 +1,38 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 5, not SunOS 4. +! Assumes this is called before the stack contents are +! examined. + + .seg "text" + .globl GC_save_regs_in_stack + .globl GC_push_regs +GC_save_regs_in_stack: +GC_push_regs: + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop + + .globl GC_clear_stack_inner +GC_clear_stack_inner: + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot + + + + + + diff --git a/gc/sparc_sunos4_mach_dep.s b/gc/sparc_sunos4_mach_dep.s new file mode 100644 index 0000000..4185807 --- /dev/null +++ b/gc/sparc_sunos4_mach_dep.s @@ -0,0 +1,38 @@ +! SPARCompiler 3.0 and later apparently no longer handles +! asm outside functions. So we need a separate .s file +! This is only set up for SunOS 4. +! Assumes this is called before the stack contents are +! examined. + + .seg "text" + .globl _GC_save_regs_in_stack + .globl _GC_push_regs +_GC_save_regs_in_stack: +_GC_push_regs: + ta 0x3 ! ST_FLUSH_WINDOWS + mov %sp,%o0 + retl + nop + + .globl _GC_clear_stack_inner +_GC_clear_stack_inner: + mov %sp,%o2 ! Save sp + add %sp,-8,%o3 ! p = sp-8 + clr %g1 ! [g0,g1] = 0 + add %o1,-0x60,%sp ! Move sp out of the way, + ! so that traps still work. + ! Includes some extra words + ! so we can be sloppy below. +loop: + std %g0,[%o3] ! *(long long *)p = 0 + cmp %o3,%o1 + bgu loop ! if (p > limit) goto loop + add %o3,-8,%o3 ! p -= 8 (delay slot) + retl + mov %o2,%sp ! Restore sp., delay slot + + + + + + diff --git a/gc/stubborn.c b/gc/stubborn.c new file mode 100644 index 0000000..bef7b98 --- /dev/null +++ b/gc/stubborn.c @@ -0,0 +1,317 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* Boehm, July 31, 1995 5:02 pm PDT */ + + +#include "gc_priv.h" + +# ifdef STUBBORN_ALLOC +/* Stubborn object (hard to change, nearly immutable) allocation. */ + +extern ptr_t GC_clear_stack(); /* in misc.c, behaves like identity */ + +#define GENERAL_MALLOC(lb,k) \ + (GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k)) + +/* Data structure representing immutable objects that */ +/* are still being initialized. */ +/* This is a bit baroque in order to avoid acquiring */ +/* the lock twice for a typical allocation. */ + +GC_PTR * GC_changing_list_start; + +# ifdef THREADS + VOLATILE GC_PTR * VOLATILE GC_changing_list_current; +# else + GC_PTR * GC_changing_list_current; +# endif + /* Points at last added element. Also (ab)used for */ + /* synchronization. Updates and reads are assumed atomic. */ + +GC_PTR * GC_changing_list_limit; + /* Points at the last word of the buffer, which is always 0 */ + /* All entries in (GC_changing_list_current, */ + /* GC_changing_list_limit] are 0 */ + + +void GC_stubborn_init() +{ +# define INIT_SIZE 10 + + GC_changing_list_start = (GC_PTR *) + GC_generic_malloc_inner( + (word)(INIT_SIZE * sizeof(GC_PTR)), + PTRFREE); + BZERO(GC_changing_list_start, + INIT_SIZE * sizeof(GC_PTR)); + if (GC_changing_list_start == 0) { + GC_err_printf0("Insufficient space to start up\n"); + ABORT("GC_stubborn_init: put of space"); + } + GC_changing_list_current = GC_changing_list_start; + GC_changing_list_limit = GC_changing_list_start + INIT_SIZE - 1; + * GC_changing_list_limit = 0; +} + +/* Compact and possibly grow GC_uninit_list. The old copy is */ +/* left alone. Lock must be held. */ +/* When called GC_changing_list_current == GC_changing_list_limit */ +/* which is one past the current element. */ +/* When we finish GC_changing_list_current again points one past last */ +/* element. */ +/* Invariant while this is running: GC_changing_list_current */ +/* points at a word containing 0. */ +/* Returns FALSE on failure. */ +GC_bool GC_compact_changing_list() +{ + register GC_PTR *p, *q; + register word count = 0; + word old_size = (char **)GC_changing_list_limit + - (char **)GC_changing_list_start+1; + /* The casts are needed as a workaround for an Amiga bug */ + register word new_size = old_size; + GC_PTR * new_list; + + for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { + if (*p != 0) count++; + } + if (2 * count > old_size) new_size = 2 * count; + new_list = (GC_PTR *) + GC_generic_malloc_inner( + new_size * sizeof(GC_PTR), PTRFREE); + /* PTRFREE is a lie. But we don't want the collector to */ + /* consider these. We do want the list itself to be */ + /* collectable. */ + if (new_list == 0) return(FALSE); + BZERO(new_list, new_size * sizeof(GC_PTR)); + q = new_list; + for (p = GC_changing_list_start; p < GC_changing_list_limit; p++) { + if (*p != 0) *q++ = *p; + } + GC_changing_list_start = new_list; + GC_changing_list_limit = new_list + new_size - 1; + GC_changing_list_current = q; + return(TRUE); +} + +/* Add p to changing list. Clear p on failure. */ +# define ADD_CHANGING(p) \ + { \ + register struct hblk * h = HBLKPTR(p); \ + register word index = PHT_HASH(h); \ + \ + set_pht_entry_from_index(GC_changed_pages, index); \ + } \ + if (*GC_changing_list_current != 0 \ + && ++GC_changing_list_current == GC_changing_list_limit) { \ + if (!GC_compact_changing_list()) (p) = 0; \ + } \ + *GC_changing_list_current = p; + +void GC_change_stubborn(p) +GC_PTR p; +{ + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + ADD_CHANGING(p); + UNLOCK(); + ENABLE_SIGNALS(); +} + +void GC_end_stubborn_change(p) +GC_PTR p; +{ +# ifdef THREADS + register VOLATILE GC_PTR * my_current = GC_changing_list_current; +# else + register GC_PTR * my_current = GC_changing_list_current; +# endif + register GC_bool tried_quick; + DCL_LOCK_STATE; + + if (*my_current == p) { + /* Hopefully the normal case. */ + /* Compaction could not have been running when we started. */ + *my_current = 0; +# ifdef THREADS + if (my_current == GC_changing_list_current) { + /* Compaction can't have run in the interim. */ + /* We got away with the quick and dirty approach. */ + return; + } + tried_quick = TRUE; +# else + return; +# endif + } else { + tried_quick = FALSE; + } + DISABLE_SIGNALS(); + LOCK(); + my_current = GC_changing_list_current; + for (; my_current >= GC_changing_list_start; my_current--) { + if (*my_current == p) { + *my_current = 0; + UNLOCK(); + ENABLE_SIGNALS(); + return; + } + } + if (!tried_quick) { + GC_err_printf1("Bad arg to GC_end_stubborn_change: 0x%lx\n", + (unsigned long)p); + ABORT("Bad arg to GC_end_stubborn_change"); + } + UNLOCK(); + ENABLE_SIGNALS(); +} + +/* Allocate lb bytes of composite (pointerful) data */ +/* No pointer fields may be changed after a call to */ +/* GC_end_stubborn_change(p) where p is the value */ +/* returned by GC_malloc_stubborn. */ +# ifdef __STDC__ + GC_PTR GC_malloc_stubborn(size_t lb) +# else + GC_PTR GC_malloc_stubborn(lb) + size_t lb; +# endif +{ +register ptr_t op; +register ptr_t *opp; +register word lw; +ptr_t result; +DCL_LOCK_STATE; + + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_sobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + result = GC_generic_malloc((word)lb, STUBBORN); + goto record; + } + *opp = obj_link(op); + obj_link(op) = 0; + GC_words_allocd += lw; + result = (GC_PTR) op; + ADD_CHANGING(result); + FASTUNLOCK(); + return((GC_PTR)result); + } else { + result = (GC_PTR) + GC_generic_malloc((word)lb, STUBBORN); + } +record: + DISABLE_SIGNALS(); + LOCK(); + ADD_CHANGING(result); + UNLOCK(); + ENABLE_SIGNALS(); + return((GC_PTR)GC_clear_stack(result)); +} + + +/* Functions analogous to GC_read_dirty and GC_page_was_dirty. */ +/* Report pages on which stubborn objects were changed. */ +void GC_read_changed() +{ + register GC_PTR * p = GC_changing_list_start; + register GC_PTR q; + register struct hblk * h; + register word index; + + if (p == 0) /* initializing */ return; + BCOPY(GC_changed_pages, GC_prev_changed_pages, + (sizeof GC_changed_pages)); + BZERO(GC_changed_pages, (sizeof GC_changed_pages)); + for (; p <= GC_changing_list_current; p++) { + if ((q = *p) != 0) { + h = HBLKPTR(q); + index = PHT_HASH(h); + set_pht_entry_from_index(GC_changed_pages, index); + } + } +} + +GC_bool GC_page_was_changed(h) +struct hblk * h; +{ + register word index = PHT_HASH(h); + + return(get_pht_entry_from_index(GC_prev_changed_pages, index)); +} + +/* Remove unreachable entries from changed list. Should only be */ +/* called with mark bits consistent and lock held. */ +void GC_clean_changing_list() +{ + register GC_PTR * p = GC_changing_list_start; + register GC_PTR q; + register ptr_t r; + register unsigned long count = 0; + register unsigned long dropped_count = 0; + + if (p == 0) /* initializing */ return; + for (; p <= GC_changing_list_current; p++) { + if ((q = *p) != 0) { + count++; + r = (ptr_t)GC_base(q); + if (r == 0 || !GC_is_marked(r)) { + *p = 0; + dropped_count++; + } + } + } +# ifdef PRINTSTATS + if (count > 0) { + GC_printf2("%lu entries in changing list: reclaimed %lu\n", + (unsigned long)count, (unsigned long)dropped_count); + } +# endif +} + +#else /* !STUBBORN_ALLOC */ + +# ifdef __STDC__ + GC_PTR GC_malloc_stubborn(size_t lb) +# else + GC_PTR GC_malloc_stubborn(lb) + size_t lb; +# endif +{ + return(GC_malloc(lb)); +} + +/*ARGSUSED*/ +void GC_end_stubborn_change(p) +GC_PTR p; +{ +} + +/*ARGSUSED*/ +void GC_change_stubborn(p) +GC_PTR p; +{ +} + + +#endif diff --git a/gc/test.c b/gc/test.c new file mode 100644 index 0000000..b65632c --- /dev/null +++ b/gc/test.c @@ -0,0 +1,1263 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ +/* An incomplete test for the garbage collector. */ +/* Some more obscure entry points are not tested at all. */ + +# if defined(mips) && defined(SYSTYPE_BSD43) + /* MIPS RISCOS 4 */ +# else +# include <stdlib.h> +# endif +# include <stdio.h> +# include <assert.h> /* Not normally used, but handy for debugging. */ +# include "gc.h" +# include "gc_typed.h" +# include "gc_priv.h" /* For output, locking, and some statistics */ +# include "gcconfig.h" + +# ifdef MSWIN32 +# include <windows.h> +# endif + +# ifdef PCR +# include "th/PCR_ThCrSec.h" +# include "th/PCR_Th.h" +# undef GC_printf0 +# define GC_printf0 printf +# undef GC_printf1 +# define GC_printf1 printf +# endif + +# ifdef SOLARIS_THREADS +# include <thread.h> +# include <synch.h> +# endif + +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) +# include <pthread.h> +# endif + +# ifdef WIN32_THREADS +# include <process.h> + static CRITICAL_SECTION incr_cs; +# endif +# if defined(PCR) || defined(SOLARIS_THREADS) || defined(WIN32_THREADS) +# define THREADS +# endif + +# ifdef AMIGA + long __stack = 200000; +# endif + +# define FAIL (void)abort() + +/* AT_END may be defined to excercise the interior pointer test */ +/* if the collector is configured with ALL_INTERIOR_POINTERS. */ +/* As it stands, this test should succeed with either */ +/* configuration. In the FIND_LEAK configuration, it should */ +/* find lots of leaks, since we free almost nothing. */ + +struct SEXPR { + struct SEXPR * sexpr_car; + struct SEXPR * sexpr_cdr; +}; + + +typedef struct SEXPR * sexpr; + +# define INT_TO_SEXPR(x) ((sexpr)(unsigned long)(x)) + +# undef nil +# define nil (INT_TO_SEXPR(0)) +# define car(x) ((x) -> sexpr_car) +# define cdr(x) ((x) -> sexpr_cdr) +# define is_nil(x) ((x) == nil) + + +int extra_count = 0; /* Amount of space wasted in cons node */ + +/* Silly implementation of Lisp cons. Intentionally wastes lots of space */ +/* to test collector. */ +sexpr cons (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + register int *p; + register int my_extra = extra_count; + + r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra); + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + for (p = (int *)r; + ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { + if (*p) { + (void)GC_printf1("Found nonzero at 0x%lx - allocator is broken\n", + (unsigned long)p); + FAIL; + } + *p = 13; + } +# ifdef AT_END + r = (sexpr)((char *)r + (my_extra & ~7)); +# endif + r -> sexpr_car = x; + r -> sexpr_cdr = y; + my_extra++; + if ( my_extra >= 5000 ) { + extra_count = 0; + } else { + extra_count = my_extra; + } + GC_END_STUBBORN_CHANGE((char *)r); + return(r); +} + +sexpr small_cons (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + + r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + r -> sexpr_car = x; + r -> sexpr_cdr = y; + return(r); +} + +sexpr small_cons_uncollectable (x, y) +sexpr x; +sexpr y; +{ + register sexpr r; + + r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); +assert(GC_is_marked(r)); + if (r == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + r -> sexpr_car = x; + r -> sexpr_cdr = (sexpr)(~(unsigned long)y); + return(r); +} + +/* Return reverse(x) concatenated with y */ +sexpr reverse1(x, y) +sexpr x, y; +{ + if (is_nil(x)) { + return(y); + } else { + return( reverse1(cdr(x), cons(car(x), y)) ); + } +} + +sexpr reverse(x) +sexpr x; +{ + return( reverse1(x, nil) ); +} + +sexpr ints(low, up) +int low, up; +{ + if (low > up) { + return(nil); + } else { + return(small_cons(small_cons(INT_TO_SEXPR(low), nil), ints(low+1, up))); + } +} + +/* To check uncollectable allocation we build lists with disguised cdr */ +/* pointers, and make sure they don't go away. */ +sexpr uncollectable_ints(low, up) +int low, up; +{ + if (low > up) { + return(nil); + } else { + return(small_cons_uncollectable(small_cons(INT_TO_SEXPR(low), nil), + uncollectable_ints(low+1, up))); + } +} + +void check_ints(list, low, up) +sexpr list; +int low, up; +{ + if ((int)(GC_word)(car(car(list))) != low) { + (void)GC_printf0( + "List reversal produced incorrect list - collector is broken\n"); + FAIL; + } + if (low == up) { + if (cdr(list) != nil) { + (void)GC_printf0("List too long - collector is broken\n"); + FAIL; + } + } else { + check_ints(cdr(list), low+1, up); + } +} + +# define UNCOLLECTABLE_CDR(x) (sexpr)(~(unsigned long)(cdr(x))) + +void check_uncollectable_ints(list, low, up) +sexpr list; +int low, up; +{ +assert(GC_is_marked(list)); + if ((int)(GC_word)(car(car(list))) != low) { + (void)GC_printf0( + "Uncollectable list corrupted - collector is broken\n"); + FAIL; + } + if (low == up) { + if (UNCOLLECTABLE_CDR(list) != nil) { + (void)GC_printf0("Uncollectable list too long - collector is broken\n"); + FAIL; + } + } else { + check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); + } +} + +/* Not used, but useful for debugging: */ +void print_int_list(x) +sexpr x; +{ + if (is_nil(x)) { + (void)GC_printf0("NIL\n"); + } else { + (void)GC_printf1("(%ld)", (long)(car(car(x)))); + if (!is_nil(cdr(x))) { + (void)GC_printf0(", "); + (void)print_int_list(cdr(x)); + } else { + (void)GC_printf0("\n"); + } + } +} + +/* Try to force a to be strangely aligned */ +struct { + char dummy; + sexpr aa; +} A; +#define a A.aa + +/* + * A tiny list reversal test to check thread creation. + */ +#ifdef THREADS + +# ifdef WIN32_THREADS + unsigned __stdcall tiny_reverse_test(void * arg) +# else + void * tiny_reverse_test(void * arg) +# endif +{ + check_ints(reverse(reverse(ints(1,10))), 1, 10); + return 0; +} + +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) \ + || defined(SOLARIS_PTHREADS) + void fork_a_thread() + { + pthread_t t; + int code; + if ((code = pthread_create(&t, 0, tiny_reverse_test, 0)) != 0) { + (void)GC_printf1("Small thread creation failed %lu\n", + (unsigned long)code); + FAIL; + } + if ((code = pthread_join(t, 0)) != 0) { + (void)GC_printf1("Small thread join failed %lu\n", + (unsigned long)code); + FAIL; + } + } + +# elif defined(WIN32_THREADS) + void fork_a_thread() + { + unsigned thread_id; + HANDLE h; + h = (HANDLE)_beginthreadex(NULL, 0, tiny_reverse_test, + 0, 0, &thread_id); + if (h == (HANDLE)-1) { + (void)GC_printf1("Small thread creation failed %lu\n", + (unsigned long)GetLastError()); + FAIL; + } + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + (void)GC_printf1("Small thread wait failed %lu\n", + (unsigned long)GetLastError()); + FAIL; + } + } + +/* # elif defined(SOLARIS_THREADS) */ + +# else + +# define fork_a_thread() + +# endif + +#else + +# define fork_a_thread() + +#endif + +/* + * Repeatedly reverse lists built out of very different sized cons cells. + * Check that we didn't lose anything. + */ +void reverse_test() +{ + int i; + sexpr b; + sexpr c; + sexpr d; + sexpr e; + sexpr *f, *g, *h; +# if defined(MSWIN32) || defined(MACOS) + /* Win32S only allows 128K stacks */ +# define BIG 1000 +# else +# if defined PCR + /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ +# define BIG 700 +# else +# define BIG 4500 +# endif +# endif + + A.dummy = 17; + a = ints(1, 49); + b = ints(1, 50); + c = ints(1, BIG); + d = uncollectable_ints(1, 100); + e = uncollectable_ints(1, 1); + /* Check that realloc updates object descriptors correctly */ + f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); + f = (sexpr *)GC_REALLOC((GC_PTR)f, 6 * sizeof(sexpr)); + f[5] = ints(1,17); + g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); + g = (sexpr *)GC_REALLOC((GC_PTR)g, 800 * sizeof(sexpr)); + g[799] = ints(1,18); + h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); + h = (sexpr *)GC_REALLOC((GC_PTR)h, 2000 * sizeof(sexpr)); + h[1999] = ints(1,19); + /* Try to force some collections and reuse of small list elements */ + for (i = 0; i < 10; i++) { + (void)ints(1, BIG); + } + /* Superficially test interior pointer recognition on stack */ + c = (sexpr)((char *)c + sizeof(char *)); + d = (sexpr)((char *)d + sizeof(char *)); + +# ifdef __STDC__ + GC_FREE((void *)e); +# else + GC_FREE((char *)e); +# endif + check_ints(b,1,50); + check_ints(a,1,49); + for (i = 0; i < 50; i++) { + check_ints(b,1,50); + b = reverse(reverse(b)); + } + check_ints(b,1,50); + check_ints(a,1,49); + for (i = 0; i < 60; i++) { + if (i % 10 == 0) fork_a_thread(); + /* This maintains the invariant that a always points to a list of */ + /* 49 integers. Thus this is thread safe without locks, */ + /* assuming atomic pointer assignments. */ + a = reverse(reverse(a)); +# if !defined(AT_END) && !defined(THREADS) + /* This is not thread safe, since realloc explicitly deallocates */ + if (i & 1) { + a = (sexpr)GC_REALLOC((GC_PTR)a, 500); + } else { + a = (sexpr)GC_REALLOC((GC_PTR)a, 8200); + } +# endif + } + check_ints(a,1,49); + check_ints(b,1,50); + c = (sexpr)((char *)c - sizeof(char *)); + d = (sexpr)((char *)d - sizeof(char *)); + check_ints(c,1,BIG); + check_uncollectable_ints(d, 1, 100); + check_ints(f[5], 1,17); + check_ints(g[799], 1,18); + check_ints(h[1999], 1,19); +# ifndef THREADS + a = 0; +# endif + b = c = 0; +} + +/* + * The rest of this builds balanced binary trees, checks that they don't + * disappear, and tests finalization. + */ +typedef struct treenode { + int level; + struct treenode * lchild; + struct treenode * rchild; +} tn; + +int finalizable_count = 0; +int finalized_count = 0; +VOLATILE int dropped_something = 0; + +# ifdef __STDC__ + void finalizer(void * obj, void * client_data) +# else + void finalizer(obj, client_data) + char * obj; + char * client_data; +# endif +{ + tn * t = (tn *)obj; + +# ifdef PCR + PCR_ThCrSec_EnterSys(); +# endif +# ifdef SOLARIS_THREADS + static mutex_t incr_lock; + mutex_lock(&incr_lock); +# endif +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) + static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&incr_lock); +# endif +# ifdef WIN32_THREADS + EnterCriticalSection(&incr_cs); +# endif + if ((int)(GC_word)client_data != t -> level) { + (void)GC_printf0("Wrong finalization data - collector is broken\n"); + FAIL; + } + finalized_count++; +# ifdef PCR + PCR_ThCrSec_ExitSys(); +# endif +# ifdef SOLARIS_THREADS + mutex_unlock(&incr_lock); +# endif +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) + pthread_mutex_unlock(&incr_lock); +# endif +# ifdef WIN32_THREADS + LeaveCriticalSection(&incr_cs); +# endif +} + +size_t counter = 0; + +# define MAX_FINALIZED 8000 + +# if !defined(MACOS) + GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0}; +#else + /* Too big for THINK_C. have to allocate it dynamically. */ + GC_word *live_indicators = 0; +#endif + +int live_indicators_count = 0; + +tn * mktree(n) +int n; +{ + tn * result = (tn *)GC_MALLOC(sizeof(tn)); + +#if defined(MACOS) + /* get around static data limitations. */ + if (!live_indicators) + live_indicators = + (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); + if (!live_indicators) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } +#endif + if (n == 0) return(0); + if (result == 0) { + (void)GC_printf0("Out of memory\n"); + exit(1); + } + result -> level = n; + result -> lchild = mktree(n-1); + result -> rchild = mktree(n-1); + if (counter++ % 17 == 0 && n >= 2) { + tn * tmp = result -> lchild -> rchild; + + result -> lchild -> rchild = result -> rchild -> lchild; + result -> rchild -> lchild = tmp; + } + if (counter++ % 119 == 0) { + int my_index; + + { +# ifdef PCR + PCR_ThCrSec_EnterSys(); +# endif +# ifdef SOLARIS_THREADS + static mutex_t incr_lock; + mutex_lock(&incr_lock); +# endif +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) + static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&incr_lock); +# endif +# ifdef WIN32_THREADS + EnterCriticalSection(&incr_cs); +# endif + /* Losing a count here causes erroneous report of failure. */ + finalizable_count++; + my_index = live_indicators_count++; +# ifdef PCR + PCR_ThCrSec_ExitSys(); +# endif +# ifdef SOLARIS_THREADS + mutex_unlock(&incr_lock); +# endif +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) + pthread_mutex_unlock(&incr_lock); +# endif +# ifdef WIN32_THREADS + LeaveCriticalSection(&incr_cs); +# endif + } + + GC_REGISTER_FINALIZER((GC_PTR)result, finalizer, (GC_PTR)(GC_word)n, + (GC_finalization_proc *)0, (GC_PTR *)0); + if (my_index >= MAX_FINALIZED) { + GC_printf0("live_indicators overflowed\n"); + FAIL; + } + live_indicators[my_index] = 13; + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (GC_PTR *)(&(live_indicators[my_index])), + (GC_PTR)result) != 0) { + GC_printf0("GC_general_register_disappearing_link failed\n"); + FAIL; + } + if (GC_unregister_disappearing_link( + (GC_PTR *) + (&(live_indicators[my_index]))) == 0) { + GC_printf0("GC_unregister_disappearing_link failed\n"); + FAIL; + } + if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( + (GC_PTR *)(&(live_indicators[my_index])), + (GC_PTR)result) != 0) { + GC_printf0("GC_general_register_disappearing_link failed 2\n"); + FAIL; + } + } + return(result); +} + +void chktree(t,n) +tn *t; +int n; +{ + if (n == 0 && t != 0) { + (void)GC_printf0("Clobbered a leaf - collector is broken\n"); + FAIL; + } + if (n == 0) return; + if (t -> level != n) { + (void)GC_printf1("Lost a node at level %lu - collector is broken\n", + (unsigned long)n); + FAIL; + } + if (counter++ % 373 == 0) (void) GC_MALLOC(counter%5001); + chktree(t -> lchild, n-1); + if (counter++ % 73 == 0) (void) GC_MALLOC(counter%373); + chktree(t -> rchild, n-1); +} + +# if defined(SOLARIS_THREADS) && !defined(_SOLARIS_PTHREADS) +thread_key_t fl_key; + +void * alloc8bytes() +{ +# if defined(SMALL_CONFIG) || defined(GC_DEBUG) + return(GC_MALLOC(8)); +# else + void ** my_free_list_ptr; + void * my_free_list; + + if (thr_getspecific(fl_key, (void **)(&my_free_list_ptr)) != 0) { + (void)GC_printf0("thr_getspecific failed\n"); + FAIL; + } + if (my_free_list_ptr == 0) { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + if (thr_setspecific(fl_key, my_free_list_ptr) != 0) { + (void)GC_printf0("thr_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) { + my_free_list = GC_malloc_many(8); + if (my_free_list == 0) { + (void)GC_printf0("alloc8bytes out of memory\n"); + FAIL; + } + } + *my_free_list_ptr = GC_NEXT(my_free_list); + GC_NEXT(my_free_list) = 0; + return(my_free_list); +# endif +} + +#else + +# if defined(_SOLARIS_PTHREADS) || defined(IRIX_THREADS) \ + || defined(LINUX_THREADS) +pthread_key_t fl_key; + +void * alloc8bytes() +{ +# ifdef SMALL_CONFIG + return(GC_malloc(8)); +# else + void ** my_free_list_ptr; + void * my_free_list; + + my_free_list_ptr = (void **)pthread_getspecific(fl_key); + if (my_free_list_ptr == 0) { + my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); + if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { + (void)GC_printf0("pthread_setspecific failed\n"); + FAIL; + } + } + my_free_list = *my_free_list_ptr; + if (my_free_list == 0) { + my_free_list = GC_malloc_many(8); + if (my_free_list == 0) { + (void)GC_printf0("alloc8bytes out of memory\n"); + FAIL; + } + } + *my_free_list_ptr = GC_NEXT(my_free_list); + GC_NEXT(my_free_list) = 0; + return(my_free_list); +# endif +} + +# else +# define alloc8bytes() GC_MALLOC_ATOMIC(8) +# endif +#endif + +void alloc_small(n) +int n; +{ + register int i; + + for (i = 0; i < n; i += 8) { + if (alloc8bytes() == 0) { + (void)GC_printf0("Out of memory\n"); + FAIL; + } + } +} + +# if defined(THREADS) && defined(GC_DEBUG) +# define TREE_HEIGHT 15 +# else +# define TREE_HEIGHT 16 +# endif +void tree_test() +{ + tn * root; + register int i; + + root = mktree(TREE_HEIGHT); + alloc_small(5000000); + chktree(root, TREE_HEIGHT); + if (finalized_count && ! dropped_something) { + (void)GC_printf0("Premature finalization - collector is broken\n"); + FAIL; + } + dropped_something = 1; + GC_noop(root); /* Root needs to remain live until */ + /* dropped_something is set. */ + root = mktree(TREE_HEIGHT); + chktree(root, TREE_HEIGHT); + for (i = TREE_HEIGHT; i >= 0; i--) { + root = mktree(i); + chktree(root, i); + } + alloc_small(5000000); +} + +unsigned n_tests = 0; + +GC_word bm_huge[10] = { + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0x00ffffff, +}; + + +/* A very simple test of explicitly typed allocation */ +void typed_test() +{ + GC_word * old, * new; + GC_word bm3 = 0x3; + GC_word bm2 = 0x2; + GC_word bm_large = 0xf7ff7fff; + GC_descr d1 = GC_make_descriptor(&bm3, 2); + GC_descr d2 = GC_make_descriptor(&bm2, 2); +# ifndef LINT + GC_descr dummy = GC_make_descriptor(&bm_large, 32); +# endif + GC_descr d3 = GC_make_descriptor(&bm_large, 32); + GC_descr d4 = GC_make_descriptor(bm_huge, 320); + GC_word * x = (GC_word *)GC_malloc_explicitly_typed(2000, d4); + register int i; + + old = 0; + for (i = 0; i < 4000; i++) { + new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1); + new[0] = 17; + new[1] = (GC_word)old; + old = new; + new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d2); + new[0] = 17; + new[1] = (GC_word)old; + old = new; + new = (GC_word *) GC_malloc_explicitly_typed(33 * sizeof(GC_word), d3); + new[0] = 17; + new[1] = (GC_word)old; + old = new; + new = (GC_word *) GC_calloc_explicitly_typed(4, 2 * sizeof(GC_word), + d1); + new[0] = 17; + new[1] = (GC_word)old; + old = new; + if (i & 0xff) { + new = (GC_word *) GC_calloc_explicitly_typed(7, 3 * sizeof(GC_word), + d2); + } else { + new = (GC_word *) GC_calloc_explicitly_typed(1001, + 3 * sizeof(GC_word), + d2); + } + new[0] = 17; + new[1] = (GC_word)old; + old = new; + } + for (i = 0; i < 20000; i++) { + if (new[0] != 17) { + (void)GC_printf1("typed alloc failed at %lu\n", + (unsigned long)i); + FAIL; + } + new[0] = 0; + old = new; + new = (GC_word *)(old[1]); + } + GC_gcollect(); + GC_noop(x); +} + +int fail_count = 0; + +#ifndef __STDC__ +/*ARGSUSED*/ +void fail_proc1(x) +GC_PTR x; +{ + fail_count++; +} + +#else + +/*ARGSUSED*/ +void fail_proc1(GC_PTR x) +{ + fail_count++; +} + +#endif /* __STDC__ */ + +#ifdef THREADS +# define TEST_FAIL_COUNT(n) 1 +#else +# define TEST_FAIL_COUNT(n) (fail_count >= (n)) +#endif + +void run_one_test() +{ + char *x; +# ifdef LINT + char *y = 0; +# else + char *y = (char *)(size_t)fail_proc1; +# endif + DCL_LOCK_STATE; + +# ifdef FIND_LEAK + (void)GC_printf0( + "This test program is not designed for leak detection mode\n"); + (void)GC_printf0("Expect lots of problems.\n"); +# endif + if (GC_size(GC_malloc(7)) != 8 + || GC_size(GC_malloc(15)) != 16) { + (void)GC_printf0("GC_size produced unexpected results\n"); + FAIL; + } + if (GC_size(GC_malloc(0)) != 4 && GC_size(GC_malloc(0)) != 8) { + (void)GC_printf0("GC_malloc(0) failed\n"); + FAIL; + } + if (GC_size(GC_malloc_uncollectable(0)) != 4 + && GC_size(GC_malloc_uncollectable(0)) != 8) { + (void)GC_printf0("GC_malloc_uncollectable(0) failed\n"); + FAIL; + } + GC_is_valid_displacement_print_proc = fail_proc1; + GC_is_visible_print_proc = fail_proc1; + x = GC_malloc(16); + if (GC_base(x + 13) != x) { + (void)GC_printf0("GC_base(heap ptr) produced incorrect result\n"); + FAIL; + } +# ifndef PCR + if (GC_base(y) != 0) { + (void)GC_printf0("GC_base(fn_ptr) produced incorrect result\n"); + FAIL; + } +# endif + if (GC_same_obj(x+5, x) != x + 5) { + (void)GC_printf0("GC_same_obj produced incorrect result\n"); + FAIL; + } + if (GC_is_visible(y) != y || GC_is_visible(x) != x) { + (void)GC_printf0("GC_is_visible produced incorrect result\n"); + FAIL; + } + if (!TEST_FAIL_COUNT(1)) { +# if!(defined(RS6000) || defined(POWERPC)) + /* ON RS6000s function pointers point to a descriptor in the */ + /* data segment, so there should have been no failures. */ + (void)GC_printf0("GC_is_visible produced wrong failure indication\n"); + FAIL; +# endif + } + if (GC_is_valid_displacement(y) != y + || GC_is_valid_displacement(x) != x + || GC_is_valid_displacement(x + 3) != x + 3) { + (void)GC_printf0( + "GC_is_valid_displacement produced incorrect result\n"); + FAIL; + } +# ifndef ALL_INTERIOR_POINTERS +# if defined(RS6000) || defined(POWERPC) + if (!TEST_FAIL_COUNT(1)) { +# else + if (!TEST_FAIL_COUNT(2)) { +# endif + (void)GC_printf0("GC_is_valid_displacement produced wrong failure indication\n"); + FAIL; + } +# endif + /* Test floating point alignment */ + *(double *)GC_MALLOC(sizeof(double)) = 1.0; + *(double *)GC_MALLOC(sizeof(double)) = 1.0; + /* Repeated list reversal test. */ + reverse_test(); +# ifdef PRINTSTATS + GC_printf0("-------------Finished reverse_test\n"); +# endif + typed_test(); +# ifdef PRINTSTATS + GC_printf0("-------------Finished typed_test\n"); +# endif + tree_test(); + LOCK(); + n_tests++; + UNLOCK(); + /* GC_printf1("Finished %x\n", pthread_self()); */ +} + +void check_heap_stats() +{ + unsigned long max_heap_sz; + register int i; + int still_live; + int late_finalize_count = 0; + + if (sizeof(char *) > 4) { + max_heap_sz = 15000000; + } else { + max_heap_sz = 11000000; + } +# ifdef GC_DEBUG + max_heap_sz *= 2; +# ifdef SPARC + max_heap_sz *= 2; +# endif +# endif + /* Garbage collect repeatedly so that all inaccessible objects */ + /* can be finalized. */ + while (GC_collect_a_little()) { } + for (i = 0; i < 16; i++) { + GC_gcollect(); + late_finalize_count += GC_invoke_finalizers(); + } + (void)GC_printf1("Completed %lu tests\n", (unsigned long)n_tests); + (void)GC_printf2("Finalized %lu/%lu objects - ", + (unsigned long)finalized_count, + (unsigned long)finalizable_count); +# ifdef FINALIZE_ON_DEMAND + if (finalized_count != late_finalize_count) { + (void)GC_printf0("Demand finalization error\n"); + FAIL; + } +# endif + if (finalized_count > finalizable_count + || finalized_count < finalizable_count/2) { + (void)GC_printf0("finalization is probably broken\n"); + FAIL; + } else { + (void)GC_printf0("finalization is probably ok\n"); + } + still_live = 0; + for (i = 0; i < MAX_FINALIZED; i++) { + if (live_indicators[i] != 0) { + still_live++; + } + } + i = finalizable_count - finalized_count - still_live; + if (0 != i) { + (void)GC_printf2 + ("%lu disappearing links remain and %lu more objects were not finalized\n", + (unsigned long) still_live, (unsigned long)i); + if (i > 10) { + GC_printf0("\tVery suspicious!\n"); + } else { + GC_printf0("\tSlightly suspicious, but probably OK.\n"); + } + } + (void)GC_printf1("Total number of bytes allocated is %lu\n", + (unsigned long) + WORDS_TO_BYTES(GC_words_allocd + GC_words_allocd_before_gc)); + (void)GC_printf1("Final heap size is %lu bytes\n", + (unsigned long)GC_get_heap_size()); + if (WORDS_TO_BYTES(GC_words_allocd + GC_words_allocd_before_gc) + < 33500000*n_tests) { + (void)GC_printf0("Incorrect execution - missed some allocations\n"); + FAIL; + } + if (GC_get_heap_size() > max_heap_sz*n_tests) { + (void)GC_printf0("Unexpected heap growth - collector may be broken\n"); + FAIL; + } + (void)GC_printf0("Collector appears to work\n"); +} + +#if defined(MACOS) +void SetMinimumStack(long minSize) +{ + long newApplLimit; + + if (minSize > LMGetDefltStack()) + { + newApplLimit = (long) GetApplLimit() + - (minSize - LMGetDefltStack()); + SetApplLimit((Ptr) newApplLimit); + MaxApplZone(); + } +} + +#define cMinStackSpace (512L * 1024L) + +#endif + +#ifdef __STDC__ + void warn_proc(char *msg, GC_word p) +#else + void warn_proc(msg, p) + char *msg; + GC_word p; +#endif +{ + GC_printf1(msg, (unsigned long)p); + FAIL; +} + + +#if !defined(PCR) && !defined(SOLARIS_THREADS) && !defined(WIN32_THREADS) \ + && !defined(IRIX_THREADS) && !defined(LINUX_THREADS) || defined(LINT) +#ifdef MSWIN32 + int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) +#else + int main() +#endif +{ +# if defined(DJGPP) + int dummy; +# endif + n_tests = 0; + +# if defined(DJGPP) + /* No good way to determine stack base from library; do it */ + /* manually on this platform. */ + GC_stackbottom = (GC_PTR)(&dummy); +# endif +# if defined(MACOS) + /* Make sure we have lots and lots of stack space. */ + SetMinimumStack(cMinStackSpace); + /* Cheat and let stdio initialize toolbox for us. */ + printf("Testing GC Macintosh port.\n"); +# endif + GC_INIT(); /* Only needed if gc is dynamic library. */ + (void) GC_set_warn_proc(warn_proc); +# if defined(MPROTECT_VDB) || defined(PROC_VDB) + GC_enable_incremental(); + (void) GC_printf0("Switched to incremental mode\n"); +# if defined(MPROTECT_VDB) + (void)GC_printf0("Emulating dirty bits with mprotect/signals\n"); +# else + (void)GC_printf0("Reading dirty bits from /proc\n"); +# endif +# endif + run_one_test(); + check_heap_stats(); + (void)fflush(stdout); +# ifdef LINT + /* Entry points we should be testing, but aren't. */ + /* Some can be tested by defining GC_DEBUG at the top of this file */ + /* This is a bit SunOS4 specific. */ + GC_noop(GC_expand_hp, GC_add_roots, GC_clear_roots, + GC_register_disappearing_link, + GC_register_finalizer_ignore_self, + GC_debug_register_displacement, + GC_print_obj, GC_debug_change_stubborn, + GC_debug_end_stubborn_change, GC_debug_malloc_uncollectable, + GC_debug_free, GC_debug_realloc, GC_generic_malloc_words_small, + GC_init, GC_make_closure, GC_debug_invoke_finalizer, + GC_page_was_ever_dirty, GC_is_fresh, + GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, + GC_set_max_heap_size, GC_get_bytes_since_gc, + GC_pre_incr, GC_post_incr); +# endif +# ifdef MSWIN32 + GC_win32_free_heap(); +# endif + return(0); +} +# endif + +#ifdef WIN32_THREADS + +unsigned __stdcall thr_run_one_test(void *arg) +{ + run_one_test(); + return 0; +} + +#define NTEST 2 + +int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) +{ +# if NTEST > 0 + HANDLE h[NTEST]; +# endif + int i; + unsigned thread_id; +# if 0 + GC_enable_incremental(); +# endif + InitializeCriticalSection(&incr_cs); + (void) GC_set_warn_proc(warn_proc); + for (i = 0; i < NTEST; i++) { + h[i] = (HANDLE)_beginthreadex(NULL, 0, thr_run_one_test, 0, 0, &thread_id); + if (h[i] == (HANDLE)-1) { + (void)GC_printf1("Thread creation failed %lu\n", (unsigned long)GetLastError()); + FAIL; + } + } + run_one_test(); + for (i = 0; i < NTEST; i++) + if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) { + (void)GC_printf1("Thread wait failed %lu\n", (unsigned long)GetLastError()); + FAIL; + } + check_heap_stats(); + (void)fflush(stdout); + return(0); +} + +#endif /* WIN32_THREADS */ + + +#ifdef PCR +test() +{ + PCR_Th_T * th1; + PCR_Th_T * th2; + int code; + + n_tests = 0; + /* GC_enable_incremental(); */ + (void) GC_set_warn_proc(warn_proc); + th1 = PCR_Th_Fork(run_one_test, 0); + th2 = PCR_Th_Fork(run_one_test, 0); + run_one_test(); + if (PCR_Th_T_Join(th1, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + (void)GC_printf0("Thread 1 failed\n"); + } + if (PCR_Th_T_Join(th2, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) + != PCR_ERes_okay || code != 0) { + (void)GC_printf0("Thread 2 failed\n"); + } + check_heap_stats(); + (void)fflush(stdout); + return(0); +} +#endif + +#if defined(SOLARIS_THREADS) || defined(IRIX_THREADS) || defined(LINUX_THREADS) +void * thr_run_one_test(void * arg) +{ + run_one_test(); + return(0); +} + +#ifdef GC_DEBUG +# define GC_free GC_debug_free +#endif + +#ifdef SOLARIS_THREADS +main() +{ + thread_t th1; + thread_t th2; + int code; + + n_tests = 0; + GC_INIT(); /* Only needed if gc is dynamic library. */ + GC_enable_incremental(); + (void) GC_set_warn_proc(warn_proc); + if (thr_keycreate(&fl_key, GC_free) != 0) { + (void)GC_printf1("Key creation failed %lu\n", (unsigned long)code); + FAIL; + } + if ((code = thr_create(0, 1024*1024, thr_run_one_test, 0, 0, &th1)) != 0) { + (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); + FAIL; + } + if ((code = thr_create(0, 1024*1024, thr_run_one_test, 0, THR_NEW_LWP, &th2)) != 0) { + (void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code); + FAIL; + } + run_one_test(); + if ((code = thr_join(th1, 0, 0)) != 0) { + (void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code); + FAIL; + } + if (thr_join(th2, 0, 0) != 0) { + (void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code); + FAIL; + } + check_heap_stats(); + (void)fflush(stdout); + return(0); +} +#else /* pthreads */ +main() +{ + pthread_t th1; + pthread_t th2; + pthread_attr_t attr; + int code; + +# ifdef IRIX_THREADS + /* Force a larger stack to be preallocated */ + /* Since the initial cant always grow later. */ + *((volatile char *)&code - 1024*1024) = 0; /* Require 1 Mb */ +# endif /* IRIX_THREADS */ + pthread_attr_init(&attr); +# ifdef IRIX_THREADS + pthread_attr_setstacksize(&attr, 1000000); +# endif + n_tests = 0; +# ifdef MPROTECT_VDB + GC_enable_incremental(); + (void) GC_printf0("Switched to incremental mode\n"); + (void) GC_printf0("Emulating dirty bits with mprotect/signals\n"); +# endif + (void) GC_set_warn_proc(warn_proc); + if (pthread_key_create(&fl_key, 0) != 0) { + (void)GC_printf1("Key creation failed %lu\n", (unsigned long)code); + FAIL; + } + if ((code = pthread_create(&th1, &attr, thr_run_one_test, 0)) != 0) { + (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); + FAIL; + } + if ((code = pthread_create(&th2, &attr, thr_run_one_test, 0)) != 0) { + (void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code); + FAIL; + } + run_one_test(); + if ((code = pthread_join(th1, 0)) != 0) { + (void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code); + FAIL; + } + if (pthread_join(th2, 0) != 0) { + (void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code); + FAIL; + } + check_heap_stats(); + (void)fflush(stdout); + pthread_attr_destroy(&attr); + GC_printf1("Completed %d collections\n", GC_gc_no); + return(0); +} +#endif /* pthreads */ +#endif /* SOLARIS_THREADS || IRIX_THREADS || LINUX_THREADS */ diff --git a/gc/test_cpp.cc b/gc/test_cpp.cc new file mode 100644 index 0000000..3160b09 --- /dev/null +++ b/gc/test_cpp.cc @@ -0,0 +1,265 @@ +/**************************************************************************** +Copyright (c) 1994 by Xerox Corporation. All rights reserved. + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program for any +purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is +granted, provided the above notices are retained, and a notice that +the code was modified is included with the above copyright notice. +**************************************************************************** +Last modified on Mon Jul 10 21:06:03 PDT 1995 by ellis + modified on December 20, 1994 7:27 pm PST by boehm + +usage: test_cpp number-of-iterations + +This program tries to test the specific C++ functionality provided by +gc_c++.h that isn't tested by the more general test routines of the +collector. + +A recommended value for number-of-iterations is 10, which will take a +few minutes to complete. + +***************************************************************************/ + +#include "gc_cpp.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef __GNUC__ +# include "gc_alloc.h" +#endif +extern "C" { +#include "gc_priv.h" +} +#ifdef MSWIN32 +# include <windows.h> +#endif + + +#define my_assert( e ) \ + if (! (e)) { \ + GC_printf1( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \ + __LINE__ ); \ + exit( 1 ); } + + +class A {public: + /* An uncollectable class. */ + + A( int iArg ): i( iArg ) {} + void Test( int iArg ) { + my_assert( i == iArg );} + int i;}; + + +class B: public gc, public A {public: + /* A collectable class. */ + + B( int j ): A( j ) {} + ~B() { + my_assert( deleting );} + static void Deleting( int on ) { + deleting = on;} + static int deleting;}; + +int B::deleting = 0; + + +class C: public gc_cleanup, public A {public: + /* A collectable class with cleanup and virtual multiple inheritance. */ + + C( int levelArg ): A( levelArg ), level( levelArg ) { + nAllocated++; + if (level > 0) { + left = new C( level - 1 ); + right = new C( level - 1 );} + else { + left = right = 0;}} + ~C() { + this->A::Test( level ); + nFreed++; + my_assert( level == 0 ? + left == 0 && right == 0 : + level == left->level + 1 && level == right->level + 1 ); + left = right = 0; + level = -123456;} + static void Test() { + my_assert( nFreed <= nAllocated && nFreed >= .8 * nAllocated );} + + static int nFreed; + static int nAllocated; + int level; + C* left; + C* right;}; + +int C::nFreed = 0; +int C::nAllocated = 0; + + +class D: public gc {public: + /* A collectable class with a static member function to be used as + an explicit clean-up function supplied to ::new. */ + + D( int iArg ): i( iArg ) { + nAllocated++;} + static void CleanUp( void* obj, void* data ) { + D* self = (D*) obj; + nFreed++; + my_assert( self->i == (int) (long) data );} + static void Test() { + my_assert( nFreed >= .8 * nAllocated );} + + int i; + static int nFreed; + static int nAllocated;}; + +int D::nFreed = 0; +int D::nAllocated = 0; + + +class E: public gc_cleanup {public: + /* A collectable class with clean-up for use by F. */ + + E() { + nAllocated++;} + ~E() { + nFreed++;} + + static int nFreed; + static int nAllocated;}; + +int E::nFreed = 0; +int E::nAllocated = 0; + + +class F: public E {public: + /* A collectable class with clean-up, a base with clean-up, and a + member with clean-up. */ + + F() { + nAllocated++;} + ~F() { + nFreed++;} + static void Test() { + my_assert( nFreed >= .8 * nAllocated ); + my_assert( 2 * nFreed == E::nFreed );} + + E e; + static int nFreed; + static int nAllocated;}; + +int F::nFreed = 0; +int F::nAllocated = 0; + + +long Disguise( void* p ) { + return ~ (long) p;} + +void* Undisguise( long i ) { + return (void*) ~ i;} + + +#ifdef MSWIN32 +int APIENTRY WinMain( + HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow ) +{ + int argc; + char* argv[ 3 ]; + + for (argc = 1; argc < sizeof( argv ) / sizeof( argv[ 0 ] ); argc++) { + argv[ argc ] = strtok( argc == 1 ? cmd : 0, " \t" ); + if (0 == argv[ argc ]) break;} + +#else +# ifdef MACOS + int main() { +# else + int main( int argc, char* argv[] ) { +# endif +#endif + +# if defined(MACOS) // MacOS + char* argv_[] = {"test_cpp", "10"}; // doesn't + argv = argv_; // have a + argc = sizeof(argv_)/sizeof(argv_[0]); // commandline +# endif + int i, iters, n; +# if !defined(__GNUC__) && !defined(MACOS) + int *x = (int *)alloc::allocate(sizeof(int)); + + *x = 29; + x -= 3; +# endif + if (argc != 2 || (0 >= (n = atoi( argv[ 1 ] )))) { + GC_printf0( "usage: test_cpp number-of-iterations\n" ); + exit( 1 );} + + for (iters = 1; iters <= n; iters++) { + GC_printf1( "Starting iteration %d\n", iters ); + + /* Allocate some uncollectable As and disguise their pointers. + Later we'll check to see if the objects are still there. We're + checking to make sure these objects really are uncollectable. */ + long as[ 1000 ]; + long bs[ 1000 ]; + for (i = 0; i < 1000; i++) { + as[ i ] = Disguise( new (NoGC) A( i ) ); + bs[ i ] = Disguise( new (NoGC) B( i ) );} + + /* Allocate a fair number of finalizable Cs, Ds, and Fs. + Later we'll check to make sure they've gone away. */ + for (i = 0; i < 1000; i++) { + C* c = new C( 2 ); + C c1( 2 ); /* stack allocation should work too */ + D* d = ::new (GC, D::CleanUp, (void*) i) D( i ); + F* f = new F; + if (0 == i % 10) delete c;} + + /* Allocate a very large number of collectable As and Bs and + drop the references to them immediately, forcing many + collections. */ + for (i = 0; i < 1000000; i++) { + A* a = new (GC) A( i ); + B* b = new B( i ); + b = new (GC) B( i ); + if (0 == i % 10) { + B::Deleting( 1 ); + delete b; + B::Deleting( 0 );} +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + } + + /* Make sure the uncollectable As and Bs are still there. */ + for (i = 0; i < 1000; i++) { + A* a = (A*) Undisguise( as[ i ] ); + B* b = (B*) Undisguise( bs[ i ] ); + a->Test( i ); + delete a; + b->Test( i ); + B::Deleting( 1 ); + delete b; + B::Deleting( 0 ); +# ifdef FINALIZE_ON_DEMAND + GC_invoke_finalizers(); +# endif + + } + + /* Make sure most of the finalizable Cs, Ds, and Fs have + gone away. */ + C::Test(); + D::Test(); + F::Test();} + +# if !defined(__GNUC__) && !defined(MACOS) + my_assert (29 == x[3]); +# endif + GC_printf0( "The test appears to have succeeded.\n" ); + return( 0 );} + + diff --git a/gc/threadlibs.c b/gc/threadlibs.c new file mode 100644 index 0000000..4a0a6cf --- /dev/null +++ b/gc/threadlibs.c @@ -0,0 +1,14 @@ +# include "gcconfig.h" +# include <stdio.h> + +int main() +{ +# if defined(IRIX_THREADS) || defined(LINUX_THREADS) + printf("-lpthread\n"); +# endif +# ifdef SOLARIS_THREADS + printf("-lthread -ldl\n"); +# endif + return 0; +} + diff --git a/gc/typd_mlc.c b/gc/typd_mlc.c new file mode 100644 index 0000000..74f455d --- /dev/null +++ b/gc/typd_mlc.c @@ -0,0 +1,814 @@ +/* + * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ +/* Boehm, July 31, 1995 5:02 pm PDT */ + + +/* + * Some simple primitives for allocation with explicit type information. + * Simple objects are allocated such that they contain a GC_descr at the + * end (in the last allocated word). This descriptor may be a procedure + * which then examines an extended descriptor passed as its environment. + * + * Arrays are treated as simple objects if they have sufficiently simple + * structure. Otherwise they are allocated from an array kind that supplies + * a special mark procedure. These arrays contain a pointer to a + * complex_descriptor as their last word. + * This is done because the environment field is too small, and the collector + * must trace the complex_descriptor. + * + * Note that descriptors inside objects may appear cleared, if we encounter a + * false refrence to an object on a free list. In the GC_descr case, this + * is OK, since a 0 descriptor corresponds to examining no fields. + * In the complex_descriptor case, we explicitly check for that case. + * + * MAJOR PARTS OF THIS CODE HAVE NOT BEEN TESTED AT ALL and are not testable, + * since they are not accessible through the current interface. + */ + +#include "gc_priv.h" +#include "gc_mark.h" +#include "gc_typed.h" + +# ifdef ADD_BYTE_AT_END +# define EXTRA_BYTES (sizeof(word) - 1) +# else +# define EXTRA_BYTES (sizeof(word)) +# endif + +GC_bool GC_explicit_typing_initialized = FALSE; + +int GC_explicit_kind; /* Object kind for objects with indirect */ + /* (possibly extended) descriptors. */ + +int GC_array_kind; /* Object kind for objects with complex */ + /* descriptors and GC_array_mark_proc. */ + +/* Extended descriptors. GC_typed_mark_proc understands these. */ +/* These are used for simple objects that are larger than what */ +/* can be described by a BITMAP_BITS sized bitmap. */ +typedef struct { + word ed_bitmap; /* lsb corresponds to first word. */ + GC_bool ed_continued; /* next entry is continuation. */ +} ext_descr; + +/* Array descriptors. GC_array_mark_proc understands these. */ +/* We may eventually need to add provisions for headers and */ +/* trailers. Hence we provide for tree structured descriptors, */ +/* though we don't really use them currently. */ +typedef union ComplexDescriptor { + struct LeafDescriptor { /* Describes simple array */ + word ld_tag; +# define LEAF_TAG 1 + word ld_size; /* bytes per element */ + /* multiple of ALIGNMENT */ + word ld_nelements; /* Number of elements. */ + GC_descr ld_descriptor; /* A simple length, bitmap, */ + /* or procedure descriptor. */ + } ld; + struct ComplexArrayDescriptor { + word ad_tag; +# define ARRAY_TAG 2 + word ad_nelements; + union ComplexDescriptor * ad_element_descr; + } ad; + struct SequenceDescriptor { + word sd_tag; +# define SEQUENCE_TAG 3 + union ComplexDescriptor * sd_first; + union ComplexDescriptor * sd_second; + } sd; +} complex_descriptor; +#define TAG ld.ld_tag + +ext_descr * GC_ext_descriptors; /* Points to array of extended */ + /* descriptors. */ + +word GC_ed_size = 0; /* Current size of above arrays. */ +# define ED_INITIAL_SIZE 100; + +word GC_avail_descr = 0; /* Next available slot. */ + +int GC_typed_mark_proc_index; /* Indices of my mark */ +int GC_array_mark_proc_index; /* procedures. */ + +/* Add a multiword bitmap to GC_ext_descriptors arrays. Return */ +/* starting index. */ +/* Returns -1 on failure. */ +/* Caller does not hold allocation lock. */ +signed_word GC_add_ext_descriptor(bm, nbits) +GC_bitmap bm; +word nbits; +{ + register size_t nwords = divWORDSZ(nbits + WORDSZ-1); + register signed_word result; + register word i; + register word last_part; + register int extra_bits; + DCL_LOCK_STATE; + + DISABLE_SIGNALS(); + LOCK(); + while (GC_avail_descr + nwords >= GC_ed_size) { + ext_descr * new; + size_t new_size; + word ed_size = GC_ed_size; + + UNLOCK(); + ENABLE_SIGNALS(); + if (ed_size == 0) { + new_size = ED_INITIAL_SIZE; + } else { + new_size = 2 * ed_size; + if (new_size > MAX_ENV) return(-1); + } + new = (ext_descr *) GC_malloc_atomic(new_size * sizeof(ext_descr)); + if (new == 0) return(-1); + DISABLE_SIGNALS(); + LOCK(); + if (ed_size == GC_ed_size) { + if (GC_avail_descr != 0) { + BCOPY(GC_ext_descriptors, new, + GC_avail_descr * sizeof(ext_descr)); + } + GC_ed_size = new_size; + GC_ext_descriptors = new; + } /* else another thread already resized it in the meantime */ + } + result = GC_avail_descr; + for (i = 0; i < nwords-1; i++) { + GC_ext_descriptors[result + i].ed_bitmap = bm[i]; + GC_ext_descriptors[result + i].ed_continued = TRUE; + } + last_part = bm[i]; + /* Clear irrelevant bits. */ + extra_bits = nwords * WORDSZ - nbits; + last_part <<= extra_bits; + last_part >>= extra_bits; + GC_ext_descriptors[result + i].ed_bitmap = last_part; + GC_ext_descriptors[result + i].ed_continued = FALSE; + GC_avail_descr += nwords; + UNLOCK(); + ENABLE_SIGNALS(); + return(result); +} + +/* Table of bitmap descriptors for n word long all pointer objects. */ +GC_descr GC_bm_table[WORDSZ/2]; + +/* Return a descriptor for the concatenation of 2 nwords long objects, */ +/* each of which is described by descriptor. */ +/* The result is known to be short enough to fit into a bitmap */ +/* descriptor. */ +/* Descriptor is a DS_LENGTH or DS_BITMAP descriptor. */ +GC_descr GC_double_descr(descriptor, nwords) +register GC_descr descriptor; +register word nwords; +{ + if (descriptor && DS_TAGS == DS_LENGTH) { + descriptor = GC_bm_table[BYTES_TO_WORDS((word)descriptor)]; + }; + descriptor |= (descriptor & ~DS_TAGS) >> nwords; + return(descriptor); +} + +complex_descriptor * GC_make_sequence_descriptor(); + +/* Build a descriptor for an array with nelements elements, */ +/* each of which can be described by a simple descriptor. */ +/* We try to optimize some common cases. */ +/* If the result is COMPLEX, then a complex_descr* is returned */ +/* in *complex_d. */ +/* If the result is LEAF, then we built a LeafDescriptor in */ +/* the structure pointed to by leaf. */ +/* The tag in the leaf structure is not set. */ +/* If the result is SIMPLE, then a GC_descr */ +/* is returned in *simple_d. */ +/* If the result is NO_MEM, then */ +/* we failed to allocate the descriptor. */ +/* The implementation knows that DS_LENGTH is 0. */ +/* *leaf, *complex_d, and *simple_d may be used as temporaries */ +/* during the construction. */ +# define COMPLEX 2 +# define LEAF 1 +# define SIMPLE 0 +# define NO_MEM (-1) +int GC_make_array_descriptor(nelements, size, descriptor, + simple_d, complex_d, leaf) +word size; +word nelements; +GC_descr descriptor; +GC_descr *simple_d; +complex_descriptor **complex_d; +struct LeafDescriptor * leaf; +{ +# define OPT_THRESHOLD 50 + /* For larger arrays, we try to combine descriptors of adjacent */ + /* descriptors to speed up marking, and to reduce the amount */ + /* of space needed on the mark stack. */ + if ((descriptor & DS_TAGS) == DS_LENGTH) { + if ((word)descriptor == size) { + *simple_d = nelements * descriptor; + return(SIMPLE); + } else if ((word)descriptor == 0) { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + if (nelements <= OPT_THRESHOLD) { + if (nelements <= 1) { + if (nelements == 1) { + *simple_d = descriptor; + return(SIMPLE); + } else { + *simple_d = (GC_descr)0; + return(SIMPLE); + } + } + } else if (size <= BITMAP_BITS/2 + && (descriptor & DS_TAGS) != DS_PROC + && (size & (sizeof(word)-1)) == 0) { + int result = + GC_make_array_descriptor(nelements/2, 2*size, + GC_double_descr(descriptor, + BYTES_TO_WORDS(size)), + simple_d, complex_d, leaf); + if ((nelements & 1) == 0) { + return(result); + } else { + struct LeafDescriptor * one_element = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + + if (result == NO_MEM || one_element == 0) return(NO_MEM); + one_element -> ld_tag = LEAF_TAG; + one_element -> ld_size = size; + one_element -> ld_nelements = 1; + one_element -> ld_descriptor = descriptor; + switch(result) { + case SIMPLE: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = size; + beginning -> ld_nelements = 1; + beginning -> ld_descriptor = *simple_d; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case LEAF: + { + struct LeafDescriptor * beginning = + (struct LeafDescriptor *) + GC_malloc_atomic(sizeof(struct LeafDescriptor)); + if (beginning == 0) return(NO_MEM); + beginning -> ld_tag = LEAF_TAG; + beginning -> ld_size = leaf -> ld_size; + beginning -> ld_nelements = leaf -> ld_nelements; + beginning -> ld_descriptor = leaf -> ld_descriptor; + *complex_d = GC_make_sequence_descriptor( + (complex_descriptor *)beginning, + (complex_descriptor *)one_element); + break; + } + case COMPLEX: + *complex_d = GC_make_sequence_descriptor( + *complex_d, + (complex_descriptor *)one_element); + break; + } + return(COMPLEX); + } + } + { + leaf -> ld_size = size; + leaf -> ld_nelements = nelements; + leaf -> ld_descriptor = descriptor; + return(LEAF); + } +} + +complex_descriptor * GC_make_sequence_descriptor(first, second) +complex_descriptor * first; +complex_descriptor * second; +{ + struct SequenceDescriptor * result = + (struct SequenceDescriptor *) + GC_malloc(sizeof(struct SequenceDescriptor)); + /* Can't result in overly conservative marking, since tags are */ + /* very small integers. Probably faster than maintaining type */ + /* info. */ + if (result != 0) { + result -> sd_tag = SEQUENCE_TAG; + result -> sd_first = first; + result -> sd_second = second; + } + return((complex_descriptor *)result); +} + +#ifdef UNDEFINED +complex_descriptor * GC_make_complex_array_descriptor(nelements, descr) +word nelements; +complex_descriptor * descr; +{ + struct ComplexArrayDescriptor * result = + (struct ComplexArrayDescriptor *) + GC_malloc(sizeof(struct ComplexArrayDescriptor)); + + if (result != 0) { + result -> ad_tag = ARRAY_TAG; + result -> ad_nelements = nelements; + result -> ad_element_descr = descr; + } + return((complex_descriptor *)result); +} +#endif + +ptr_t * GC_eobjfreelist; + +ptr_t * GC_arobjfreelist; + +mse * GC_typed_mark_proc(); + +mse * GC_array_mark_proc(); + +GC_descr GC_generic_array_descr; + +/* Caller does not hold allocation lock. */ +void GC_init_explicit_typing() +{ + register int i; + DCL_LOCK_STATE; + + +# ifdef PRINTSTATS + if (sizeof(struct LeafDescriptor) % sizeof(word) != 0) + ABORT("Bad leaf descriptor size"); +# endif + DISABLE_SIGNALS(); + LOCK(); + if (GC_explicit_typing_initialized) { + UNLOCK(); + ENABLE_SIGNALS(); + return; + } + GC_explicit_typing_initialized = TRUE; + /* Set up object kind with simple indirect descriptor. */ + GC_eobjfreelist = (ptr_t *) + GC_generic_malloc_inner((MAXOBJSZ+1)*sizeof(ptr_t), PTRFREE); + if (GC_eobjfreelist == 0) ABORT("Couldn't allocate GC_eobjfreelist"); + BZERO(GC_eobjfreelist, (MAXOBJSZ+1)*sizeof(ptr_t)); + GC_explicit_kind = GC_n_kinds++; + GC_obj_kinds[GC_explicit_kind].ok_freelist = GC_eobjfreelist; + GC_obj_kinds[GC_explicit_kind].ok_reclaim_list = 0; + GC_obj_kinds[GC_explicit_kind].ok_descriptor = + (((word)WORDS_TO_BYTES(-1)) | DS_PER_OBJECT); + GC_obj_kinds[GC_explicit_kind].ok_relocate_descr = TRUE; + GC_obj_kinds[GC_explicit_kind].ok_init = TRUE; + /* Descriptors are in the last word of the object. */ + GC_typed_mark_proc_index = GC_n_mark_procs; + GC_mark_procs[GC_typed_mark_proc_index] = GC_typed_mark_proc; + GC_n_mark_procs++; + /* Moving this up breaks DEC AXP compiler. */ + /* Set up object kind with array descriptor. */ + GC_arobjfreelist = (ptr_t *) + GC_generic_malloc_inner((MAXOBJSZ+1)*sizeof(ptr_t), PTRFREE); + if (GC_arobjfreelist == 0) ABORT("Couldn't allocate GC_arobjfreelist"); + BZERO(GC_arobjfreelist, (MAXOBJSZ+1)*sizeof(ptr_t)); + if (GC_n_mark_procs >= MAX_MARK_PROCS) + ABORT("No slot for array mark proc"); + GC_array_mark_proc_index = GC_n_mark_procs++; + if (GC_n_kinds >= MAXOBJKINDS) + ABORT("No kind available for array objects"); + GC_array_kind = GC_n_kinds++; + GC_obj_kinds[GC_array_kind].ok_freelist = GC_arobjfreelist; + GC_obj_kinds[GC_array_kind].ok_reclaim_list = 0; + GC_obj_kinds[GC_array_kind].ok_descriptor = + MAKE_PROC(GC_array_mark_proc_index, 0);; + GC_obj_kinds[GC_array_kind].ok_relocate_descr = FALSE; + GC_obj_kinds[GC_array_kind].ok_init = TRUE; + /* Descriptors are in the last word of the object. */ + GC_mark_procs[GC_array_mark_proc_index] = GC_array_mark_proc; + for (i = 0; i < WORDSZ/2; i++) { + GC_descr d = (((word)(-1)) >> (WORDSZ - i)) << (WORDSZ - i); + d |= DS_BITMAP; + GC_bm_table[i] = d; + } + GC_generic_array_descr = MAKE_PROC(GC_array_mark_proc_index, 0); + UNLOCK(); + ENABLE_SIGNALS(); +} + +mse * GC_typed_mark_proc(addr, mark_stack_ptr, mark_stack_limit, env) +register word * addr; +register mse * mark_stack_ptr; +mse * mark_stack_limit; +word env; +{ + register word bm = GC_ext_descriptors[env].ed_bitmap; + register word * current_p = addr; + register word current; + register ptr_t greatest_ha = GC_greatest_plausible_heap_addr; + register ptr_t least_ha = GC_least_plausible_heap_addr; + + for (; bm != 0; bm >>= 1, current_p++) { + if (bm & 1) { + current = *current_p; + if ((ptr_t)current >= least_ha && (ptr_t)current <= greatest_ha) { + PUSH_CONTENTS(current, mark_stack_ptr, + mark_stack_limit, current_p, exit1); + } + } + } + if (GC_ext_descriptors[env].ed_continued) { + /* Push an entry with the rest of the descriptor back onto the */ + /* stack. Thus we never do too much work at once. Note that */ + /* we also can't overflow the mark stack unless we actually */ + /* mark something. */ + mark_stack_ptr++; + if (mark_stack_ptr >= mark_stack_limit) { + mark_stack_ptr = GC_signal_mark_stack_overflow(mark_stack_ptr); + } + mark_stack_ptr -> mse_start = addr + WORDSZ; + mark_stack_ptr -> mse_descr = + MAKE_PROC(GC_typed_mark_proc_index, env+1); + } + return(mark_stack_ptr); +} + +/* Return the size of the object described by d. It would be faster to */ +/* store this directly, or to compute it as part of */ +/* GC_push_complex_descriptor, but hopefully it doesn't matter. */ +word GC_descr_obj_size(d) +register complex_descriptor *d; +{ + switch(d -> TAG) { + case LEAF_TAG: + return(d -> ld.ld_nelements * d -> ld.ld_size); + case ARRAY_TAG: + return(d -> ad.ad_nelements + * GC_descr_obj_size(d -> ad.ad_element_descr)); + case SEQUENCE_TAG: + return(GC_descr_obj_size(d -> sd.sd_first) + + GC_descr_obj_size(d -> sd.sd_second)); + default: + ABORT("Bad complex descriptor"); + /*NOTREACHED*/ return 0; /*NOTREACHED*/ + } +} + +/* Push descriptors for the object at addr with complex descriptor d */ +/* onto the mark stack. Return 0 if the mark stack overflowed. */ +mse * GC_push_complex_descriptor(addr, d, msp, msl) +word * addr; +register complex_descriptor *d; +register mse * msp; +mse * msl; +{ + register ptr_t current = (ptr_t) addr; + register word nelements; + register word sz; + register word i; + + switch(d -> TAG) { + case LEAF_TAG: + { + register GC_descr descr = d -> ld.ld_descriptor; + + nelements = d -> ld.ld_nelements; + if (msl - msp <= (ptrdiff_t)nelements) return(0); + sz = d -> ld.ld_size; + for (i = 0; i < nelements; i++) { + msp++; + msp -> mse_start = (word *)current; + msp -> mse_descr = descr; + current += sz; + } + return(msp); + } + case ARRAY_TAG: + { + register complex_descriptor *descr = d -> ad.ad_element_descr; + + nelements = d -> ad.ad_nelements; + sz = GC_descr_obj_size(descr); + for (i = 0; i < nelements; i++) { + msp = GC_push_complex_descriptor((word *)current, descr, + msp, msl); + if (msp == 0) return(0); + current += sz; + } + return(msp); + } + case SEQUENCE_TAG: + { + sz = GC_descr_obj_size(d -> sd.sd_first); + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_first, + msp, msl); + if (msp == 0) return(0); + current += sz; + msp = GC_push_complex_descriptor((word *)current, d -> sd.sd_second, + msp, msl); + return(msp); + } + default: + ABORT("Bad complex descriptor"); + /*NOTREACHED*/ return 0; /*NOTREACHED*/ + } +} + +/*ARGSUSED*/ +mse * GC_array_mark_proc(addr, mark_stack_ptr, mark_stack_limit, env) +register word * addr; +register mse * mark_stack_ptr; +mse * mark_stack_limit; +word env; +{ + register hdr * hhdr = HDR(addr); + register word sz = hhdr -> hb_sz; + register complex_descriptor * descr = (complex_descriptor *)(addr[sz-1]); + mse * orig_mark_stack_ptr = mark_stack_ptr; + mse * new_mark_stack_ptr; + + if (descr == 0) { + /* Found a reference to a free list entry. Ignore it. */ + return(orig_mark_stack_ptr); + } + /* In use counts were already updated when array descriptor was */ + /* pushed. Here we only replace it by subobject descriptors, so */ + /* no update is necessary. */ + new_mark_stack_ptr = GC_push_complex_descriptor(addr, descr, + mark_stack_ptr, + mark_stack_limit-1); + if (new_mark_stack_ptr == 0) { + /* Doesn't fit. Conservatively push the whole array as a unit */ + /* and request a mark stack expansion. */ + /* This cannot cause a mark stack overflow, since it replaces */ + /* the original array entry. */ + GC_mark_stack_too_small = TRUE; + new_mark_stack_ptr = orig_mark_stack_ptr + 1; + new_mark_stack_ptr -> mse_start = addr; + new_mark_stack_ptr -> mse_descr = WORDS_TO_BYTES(sz) | DS_LENGTH; + } else { + /* Push descriptor itself */ + new_mark_stack_ptr++; + new_mark_stack_ptr -> mse_start = addr + sz - 1; + new_mark_stack_ptr -> mse_descr = sizeof(word) | DS_LENGTH; + } + return(new_mark_stack_ptr); +} + +#if defined(__STDC__) || defined(__cplusplus) + GC_descr GC_make_descriptor(GC_bitmap bm, size_t len) +#else + GC_descr GC_make_descriptor(bm, len) + GC_bitmap bm; + size_t len; +#endif +{ + register signed_word last_set_bit = len - 1; + register word result; + register int i; +# define HIGH_BIT (((word)1) << (WORDSZ - 1)) + + if (!GC_explicit_typing_initialized) GC_init_explicit_typing(); + while (last_set_bit >= 0 && !GC_get_bit(bm, last_set_bit)) last_set_bit --; + if (last_set_bit < 0) return(0 /* no pointers */); +# if ALIGNMENT == CPP_WORDSZ/8 + { + register GC_bool all_bits_set = TRUE; + for (i = 0; i < last_set_bit; i++) { + if (!GC_get_bit(bm, i)) { + all_bits_set = FALSE; + break; + } + } + if (all_bits_set) { + /* An initial section contains all pointers. Use length descriptor. */ + return(WORDS_TO_BYTES(last_set_bit+1) | DS_LENGTH); + } + } +# endif + if (last_set_bit < BITMAP_BITS) { + /* Hopefully the common case. */ + /* Build bitmap descriptor (with bits reversed) */ + result = HIGH_BIT; + for (i = last_set_bit - 1; i >= 0; i--) { + result >>= 1; + if (GC_get_bit(bm, i)) result |= HIGH_BIT; + } + result |= DS_BITMAP; + return(result); + } else { + signed_word index; + + index = GC_add_ext_descriptor(bm, (word)last_set_bit+1); + if (index == -1) return(WORDS_TO_BYTES(last_set_bit+1) | DS_LENGTH); + /* Out of memory: use conservative */ + /* approximation. */ + result = MAKE_PROC(GC_typed_mark_proc_index, (word)index); + return(result); + } +} + +ptr_t GC_clear_stack(); + +#define GENERAL_MALLOC(lb,k) \ + (GC_PTR)GC_clear_stack(GC_generic_malloc((word)lb, k)) + +#define GENERAL_MALLOC_IOP(lb,k) \ + (GC_PTR)GC_clear_stack(GC_generic_malloc_ignore_off_page(lb, k)) + +#if defined(__STDC__) || defined(__cplusplus) + void * GC_malloc_explicitly_typed(size_t lb, GC_descr d) +#else + char * GC_malloc_explicitly_typed(lb, d) + size_t lb; + GC_descr d; +#endif +{ +register ptr_t op; +register ptr_t * opp; +register word lw; +DCL_LOCK_STATE; + + lb += EXTRA_BYTES; + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_eobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + op = (ptr_t)GENERAL_MALLOC((word)lb, GC_explicit_kind); + if (0 == op) return(0); +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; /* May have been uninitialized. */ +# endif + } else { + *opp = obj_link(op); + GC_words_allocd += lw; + FASTUNLOCK(); + } + } else { + op = (ptr_t)GENERAL_MALLOC((word)lb, GC_explicit_kind); + if (op != NULL) + lw = BYTES_TO_WORDS(GC_size(op)); + } + if (op != NULL) + ((word *)op)[lw - 1] = d; + return((GC_PTR) op); +} + +#if defined(__STDC__) || defined(__cplusplus) + void * GC_malloc_explicitly_typed_ignore_off_page(size_t lb, GC_descr d) +#else + char * GC_malloc_explicitly_typed_ignore_off_page(lb, d) + size_t lb; + GC_descr d; +#endif +{ +register ptr_t op; +register ptr_t * opp; +register word lw; +DCL_LOCK_STATE; + + lb += EXTRA_BYTES; + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_eobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; /* May have been uninitialized. */ +# endif + } else { + *opp = obj_link(op); + GC_words_allocd += lw; + FASTUNLOCK(); + } + } else { + op = (ptr_t)GENERAL_MALLOC_IOP(lb, GC_explicit_kind); + if (op != NULL) + lw = BYTES_TO_WORDS(GC_size(op)); + } + if (op != NULL) + ((word *)op)[lw - 1] = d; + return((GC_PTR) op); +} + +#if defined(__STDC__) || defined(__cplusplus) + void * GC_calloc_explicitly_typed(size_t n, + size_t lb, + GC_descr d) +#else + char * GC_calloc_explicitly_typed(n, lb, d) + size_t n; + size_t lb; + GC_descr d; +#endif +{ +register ptr_t op; +register ptr_t * opp; +register word lw; +GC_descr simple_descr; +complex_descriptor *complex_descr; +register int descr_type; +struct LeafDescriptor leaf; +DCL_LOCK_STATE; + + descr_type = GC_make_array_descriptor((word)n, (word)lb, d, + &simple_descr, &complex_descr, &leaf); + switch(descr_type) { + case NO_MEM: return(0); + case SIMPLE: return(GC_malloc_explicitly_typed(n*lb, simple_descr)); + case LEAF: + lb *= n; + lb += sizeof(struct LeafDescriptor) + EXTRA_BYTES; + break; + case COMPLEX: + lb *= n; + lb += EXTRA_BYTES; + break; + } + if( SMALL_OBJ(lb) ) { +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; +# else + lw = ALIGNED_WORDS(lb); +# endif + opp = &(GC_arobjfreelist[lw]); + FASTLOCK(); + if( !FASTLOCK_SUCCEEDED() || (op = *opp) == 0 ) { + FASTUNLOCK(); + op = (ptr_t)GENERAL_MALLOC((word)lb, GC_array_kind); + if (0 == op) return(0); +# ifdef MERGE_SIZES + lw = GC_size_map[lb]; /* May have been uninitialized. */ +# endif + } else { + *opp = obj_link(op); + GC_words_allocd += lw; + FASTUNLOCK(); + } + } else { + op = (ptr_t)GENERAL_MALLOC((word)lb, GC_array_kind); + if (0 == op) return(0); + lw = BYTES_TO_WORDS(GC_size(op)); + } + if (descr_type == LEAF) { + /* Set up the descriptor inside the object itself. */ + VOLATILE struct LeafDescriptor * lp = + (struct LeafDescriptor *) + ((word *)op + + lw - (BYTES_TO_WORDS(sizeof(struct LeafDescriptor)) + 1)); + + lp -> ld_tag = LEAF_TAG; + lp -> ld_size = leaf.ld_size; + lp -> ld_nelements = leaf.ld_nelements; + lp -> ld_descriptor = leaf.ld_descriptor; + ((VOLATILE word *)op)[lw - 1] = (word)lp; + } else { + extern unsigned GC_finalization_failures; + unsigned ff = GC_finalization_failures; + + ((word *)op)[lw - 1] = (word)complex_descr; + /* Make sure the descriptor is cleared once there is any danger */ + /* it may have been collected. */ + (void) + GC_general_register_disappearing_link((GC_PTR *) + ((word *)op+lw-1), + (GC_PTR) op); + if (ff != GC_finalization_failures) { + /* Couldn't register it due to lack of memory. Punt. */ + /* This will probably fail too, but gives the recovery code */ + /* a chance. */ + return(GC_malloc(n*lb)); + } + } + return((GC_PTR) op); +} diff --git a/gc/version.h b/gc/version.h new file mode 100644 index 0000000..97ac5f5 --- /dev/null +++ b/gc/version.h @@ -0,0 +1,11 @@ +#define GC_VERSION_MAJOR 5 +#define GC_VERSION_MINOR 0 +#define GC_ALPHA_VERSION 3 + +# define GC_NOT_ALPHA 0xff + +#ifndef GC_NO_VERSION_VAR + +unsigned GC_version = ((GC_VERSION_MAJOR << 16) | (GC_VERSION_MINOR << 8) | GC_ALPHA_VERSION); + +#endif /* GC_NO_VERSION_VAR */ diff --git a/gc/weakpointer.h b/gc/weakpointer.h new file mode 100644 index 0000000..84906b0 --- /dev/null +++ b/gc/weakpointer.h @@ -0,0 +1,221 @@ +#ifndef _weakpointer_h_ +#define _weakpointer_h_ + +/**************************************************************************** + +WeakPointer and CleanUp + + Copyright (c) 1991 by Xerox Corporation. All rights reserved. + + THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + + Permission is hereby granted to copy this code for any purpose, + provided the above notices are retained on all copies. + + Last modified on Mon Jul 17 18:16:01 PDT 1995 by ellis + +****************************************************************************/ + +/**************************************************************************** + +WeakPointer + +A weak pointer is a pointer to a heap-allocated object that doesn't +prevent the object from being garbage collected. Weak pointers can be +used to track which objects haven't yet been reclaimed by the +collector. A weak pointer is deactivated when the collector discovers +its referent object is unreachable by normal pointers (reachability +and deactivation are defined more precisely below). A deactivated weak +pointer remains deactivated forever. + +****************************************************************************/ + + +template< class T > class WeakPointer { +public: + +WeakPointer( T* t = 0 ) + /* Constructs a weak pointer for *t. t may be null. It is an error + if t is non-null and *t is not a collected object. */ + {impl = _WeakPointer_New( t );} + +T* Pointer() + /* wp.Pointer() returns a pointer to the referent object of wp or + null if wp has been deactivated (because its referent object + has been discovered unreachable by the collector). */ + {return (T*) _WeakPointer_Pointer( this->impl );} + +int operator==( WeakPointer< T > wp2 ) + /* Given weak pointers wp1 and wp2, if wp1 == wp2, then wp1 and + wp2 refer to the same object. If wp1 != wp2, then either wp1 + and wp2 don't refer to the same object, or if they do, one or + both of them has been deactivated. (Note: If objects t1 and t2 + are never made reachable by their clean-up functions, then + WeakPointer<T>(t1) == WeakPointer<T>(t2) if and only t1 == t2.) */ + {return _WeakPointer_Equal( this->impl, wp2.impl );} + +int Hash() + /* Returns a hash code suitable for use by multiplicative- and + division-based hash tables. If wp1 == wp2, then wp1.Hash() == + wp2.Hash(). */ + {return _WeakPointer_Hash( this->impl );} + +private: +void* impl; +}; + +/***************************************************************************** + +CleanUp + +A garbage-collected object can have an associated clean-up function +that will be invoked some time after the collector discovers the +object is unreachable via normal pointers. Clean-up functions can be +used to release resources such as open-file handles or window handles +when their containing objects become unreachable. If a C++ object has +a non-empty explicit destructor (i.e. it contains programmer-written +code), the destructor will be automatically registered as the object's +initial clean-up function. + +There is no guarantee that the collector will detect every unreachable +object (though it will find almost all of them). Clients should not +rely on clean-up to cause some action to occur immediately -- clean-up +is only a mechanism for improving resource usage. + +Every object with a clean-up function also has a clean-up queue. When +the collector finds the object is unreachable, it enqueues it on its +queue. The clean-up function is applied when the object is removed +from the queue. By default, objects are enqueued on the garbage +collector's queue, and the collector removes all objects from its +queue after each collection. If a client supplies another queue for +objects, it is his responsibility to remove objects (and cause their +functions to be called) by polling it periodically. + +Clean-up queues allow clean-up functions accessing global data to +synchronize with the main program. Garbage collection can occur at any +time, and clean-ups invoked by the collector might access data in an +inconsistent state. A client can control this by defining an explicit +queue for objects and polling it at safe points. + +The following definitions are used by the specification below: + +Given a pointer t to a collected object, the base object BO(t) is the +value returned by new when it created the object. (Because of multiple +inheritance, t and BO(t) may not be the same address.) + +A weak pointer wp references an object *t if BO(wp.Pointer()) == +BO(t). + +***************************************************************************/ + +template< class T, class Data > class CleanUp { +public: + +static void Set( T* t, void c( Data* d, T* t ), Data* d = 0 ) + /* Sets the clean-up function of object BO(t) to be <c, d>, + replacing any previously defined clean-up function for BO(t); c + and d can be null, but t cannot. Sets the clean-up queue for + BO(t) to be the collector's queue. When t is removed from its + clean-up queue, its clean-up will be applied by calling c(d, + t). It is an error if *t is not a collected object. */ + {_CleanUp_Set( t, c, d );} + +static void Call( T* t ) + /* Sets the new clean-up function for BO(t) to be null and, if the + old one is non-null, calls it immediately, even if BO(t) is + still reachable. Deactivates any weak pointers to BO(t). */ + {_CleanUp_Call( t );} + +class Queue {public: + Queue() + /* Constructs a new queue. */ + {this->head = _CleanUp_Queue_NewHead();} + + void Set( T* t ) + /* q.Set(t) sets the clean-up queue of BO(t) to be q. */ + {_CleanUp_Queue_Set( this->head, t );} + + int Call() + /* If q is non-empty, q.Call() removes the first object and + calls its clean-up function; does nothing if q is + empty. Returns true if there are more objects in the + queue. */ + {return _CleanUp_Queue_Call( this->head );} + + private: + void* head; + }; +}; + +/********************************************************************** + +Reachability and Clean-up + +An object O is reachable if it can be reached via a non-empty path of +normal pointers from the registers, stacks, global variables, or an +object with a non-null clean-up function (including O itself), +ignoring pointers from an object to itself. + +This definition of reachability ensures that if object B is accessible +from object A (and not vice versa) and if both A and B have clean-up +functions, then A will always be cleaned up before B. Note that as +long as an object with a clean-up function is contained in a cycle of +pointers, it will always be reachable and will never be cleaned up or +collected. + +When the collector finds an unreachable object with a null clean-up +function, it atomically deactivates all weak pointers referencing the +object and recycles its storage. If object B is accessible from object +A via a path of normal pointers, A will be discovered unreachable no +later than B, and a weak pointer to A will be deactivated no later +than a weak pointer to B. + +When the collector finds an unreachable object with a non-null +clean-up function, the collector atomically deactivates all weak +pointers referencing the object, redefines its clean-up function to be +null, and enqueues it on its clean-up queue. The object then becomes +reachable again and remains reachable at least until its clean-up +function executes. + +The clean-up function is assured that its argument is the only +accessible pointer to the object. Nothing prevents the function from +redefining the object's clean-up function or making the object +reachable again (for example, by storing the pointer in a global +variable). + +If the clean-up function does not make its object reachable again and +does not redefine its clean-up function, then the object will be +collected by a subsequent collection (because the object remains +unreachable and now has a null clean-up function). If the clean-up +function does make its object reachable again and a clean-up function +is subsequently redefined for the object, then the new clean-up +function will be invoked the next time the collector finds the object +unreachable. + +Note that a destructor for a collected object cannot safely redefine a +clean-up function for its object, since after the destructor executes, +the object has been destroyed into "raw memory". (In most +implementations, destroying an object mutates its vtbl.) + +Finally, note that calling delete t on a collected object first +deactivates any weak pointers to t and then invokes its clean-up +function (destructor). + +**********************************************************************/ + +extern "C" { + void* _WeakPointer_New( void* t ); + void* _WeakPointer_Pointer( void* wp ); + int _WeakPointer_Equal( void* wp1, void* wp2 ); + int _WeakPointer_Hash( void* wp ); + void _CleanUp_Set( void* t, void (*c)( void* d, void* t ), void* d ); + void _CleanUp_Call( void* t ); + void* _CleanUp_Queue_NewHead (); + void _CleanUp_Queue_Set( void* h, void* t ); + int _CleanUp_Queue_Call( void* h ); +} + +#endif /* _weakpointer_h_ */ + + diff --git a/gc/win32_threads.c b/gc/win32_threads.c new file mode 100755 index 0000000..f6f74bd --- /dev/null +++ b/gc/win32_threads.c @@ -0,0 +1,209 @@ +#ifdef WIN32_THREADS + +#include "gc_priv.h" + +#define STRICT +#include <windows.h> + +#define MAX_THREADS 64 + +struct thread_entry { + LONG in_use; + DWORD id; + HANDLE handle; + void *stack; /* The cold end of the stack. */ + /* 0 ==> entry not valid. */ + /* !in_use ==> stack == 0 */ + CONTEXT context; + GC_bool suspended; +}; + +volatile GC_bool GC_please_stop = FALSE; + +volatile struct thread_entry thread_table[MAX_THREADS]; + +void GC_stop_world() +{ + DWORD thread_id = GetCurrentThreadId(); + int i; + + GC_please_stop = TRUE; + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].stack != 0 + && thread_table[i].id != thread_id) { + if (SuspendThread(thread_table[i].handle) == (DWORD)-1) + ABORT("SuspendThread failed"); + thread_table[i].suspended = TRUE; + } +} + +void GC_start_world() +{ + DWORD thread_id = GetCurrentThreadId(); + int i; + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].stack != 0 && thread_table[i].suspended + && thread_table[i].id != thread_id) { + if (ResumeThread(thread_table[i].handle) == (DWORD)-1) + ABORT("ResumeThread failed"); + thread_table[i].suspended = FALSE; + } + GC_please_stop = FALSE; +} + +ptr_t GC_current_stackbottom() +{ + DWORD thread_id = GetCurrentThreadId(); + int i; + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].stack && thread_table[i].id == thread_id) + return thread_table[i].stack; + ABORT("no thread table entry for current thread"); +} + +ptr_t GC_get_lo_stack_addr(ptr_t s) +{ + ptr_t bottom; + MEMORY_BASIC_INFORMATION info; + VirtualQuery(s, &info, sizeof(info)); + do { + bottom = info.BaseAddress; + VirtualQuery(bottom - 1, &info, sizeof(info)); + } while ((info.Protect & PAGE_READWRITE) && !(info.Protect & PAGE_GUARD)); + return(bottom); +} + +void GC_push_all_stacks() +{ + DWORD thread_id = GetCurrentThreadId(); + int i; + for (i = 0; i < MAX_THREADS; i++) + if (thread_table[i].stack) { + ptr_t bottom = GC_get_lo_stack_addr(thread_table[i].stack); + if (thread_table[i].id == thread_id) + GC_push_all(&i, thread_table[i].stack); + else { + thread_table[i].context.ContextFlags + = (CONTEXT_INTEGER|CONTEXT_CONTROL); + if (!GetThreadContext(thread_table[i].handle, + &thread_table[i].context)) + ABORT("GetThreadContext failed"); + if (thread_table[i].context.Esp >= (DWORD)thread_table[i].stack + || thread_table[i].context.Esp < (DWORD)bottom) + ABORT("Thread stack pointer out of range"); + GC_push_one ((word) thread_table[i].context.Edi); + GC_push_one ((word) thread_table[i].context.Esi); + GC_push_one ((word) thread_table[i].context.Ebx); + GC_push_one ((word) thread_table[i].context.Edx); + GC_push_one ((word) thread_table[i].context.Ecx); + GC_push_one ((word) thread_table[i].context.Eax); + GC_push_all_stack(thread_table[i].context.Esp, thread_table[i].stack); + } + } +} + +void GC_get_next_stack(char *start, char **lo, char **hi) +{ + int i; +# define ADDR_LIMIT (char *)(-1L) + char * current_min = ADDR_LIMIT; + + for (i = 0; i < MAX_THREADS; i++) { + char * s = (char *)thread_table[i].stack; + + if (0 != s && s > start && s < current_min) { + current_min = s; + } + } + *hi = current_min; + if (current_min == ADDR_LIMIT) { + *lo = ADDR_LIMIT; + return; + } + *lo = GC_get_lo_stack_addr(current_min); + if (*lo < start) *lo = start; +} + +LONG WINAPI GC_write_fault_handler(struct _EXCEPTION_POINTERS *exc_info); + +/* + * This isn't generally safe, since DllMain is not premptible. + * If another thread holds the lock while this runs we're in trouble. + * Pontus Rydin suggests wrapping the thread start routine instead. + */ +BOOL WINAPI DllMain(HINSTANCE inst, ULONG reason, LPVOID reserved) +{ + switch (reason) { + case DLL_PROCESS_ATTACH: + InitializeCriticalSection(&GC_allocate_ml); + GC_init(); /* Force initialization before thread attach. */ + /* fall through */ + case DLL_THREAD_ATTACH: + { + int i; + /* It appears to be unsafe to acquire a lock here, since this */ + /* code is apparently not preeemptible on some systems. */ + /* (This is based on complaints, not on Microsoft's official */ + /* documentation, which says this should perform "only simple */ + /* inititalization tasks".) */ + /* Hence we make do with nonblocking synchronization. */ + + /* The following should be a noop according to the win32 */ + /* documentation. There is empirical evidence that it */ + /* isn't. - HB */ +# ifndef SMALL_CONFIG + if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); +# endif + + for (i = 0; InterlockedExchange(&thread_table[i].in_use,1) != 0; i++) { + /* Compare-and-swap would make this cleaner, but that's not */ + /* supported before Windows 98 and NT 4.0. In Windows 2000, */ + /* InterlockedExchange is supposed to be replaced by */ + /* InterlockedExchangePointer, but that's not really what I */ + /* want here. */ + if (i == MAX_THREADS - 1) + ABORT("too many threads"); + } + thread_table[i].id = GetCurrentThreadId(); + if (!DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &thread_table[i].handle, + 0, + 0, + DUPLICATE_SAME_ACCESS)) { + DWORD last_error = GetLastError(); + GC_printf1("Last error code: %lx\n", last_error); + ABORT("DuplicateHandle failed"); + } + thread_table[i].stack = GC_get_stack_base(); + /* If this thread is being created while we are trying to stop */ + /* the world, wait here. Hopefully this can't happen on any */ + /* systems that don't allow us to block here. */ + while (GC_please_stop) Sleep(20); + } + break; + case DLL_PROCESS_DETACH: + case DLL_THREAD_DETACH: + { + int i; + DWORD thread_id = GetCurrentThreadId(); + LOCK(); + for (i = 0; + thread_table[i].stack == 0 || thread_table[i].id != thread_id; + i++) { + if (i == MAX_THREADS - 1) + ABORT("thread not found on detach"); + } + thread_table[i].stack = 0; + thread_table[i].in_use = FALSE; + CloseHandle(thread_table[i].handle); + BZERO(&thread_table[i].context, sizeof(CONTEXT)); + UNLOCK(); + } + break; + } + return TRUE; +} + +#endif /* WIN32_THREADS */ diff --git a/gcmain.c b/gcmain.c new file mode 100644 index 0000000..08b28f2 --- /dev/null +++ b/gcmain.c @@ -0,0 +1,27 @@ +#ifndef GC_MAIN +#define GC_MAIN + +#if defined(AIX) || defined(linux) +/* to cope with Boehm GC... */ + +#define MAIN real_main + +#if defined(DEBIAN) +#include "gc/private/gc_priv.h" +#else +#include "private/gc_priv.h" +#endif +int real_main(int, char **, char **); + +int +main(int argc, char **argv, char **envp) +{ + int dummy; + GC_stackbottom = (ptr_t) (&dummy); + return (real_main(argc, argv, envp)); +} +#else +#define MAIN main +#endif + +#endif @@ -0,0 +1,26 @@ +#include <string.h> +#include "hash.h" +#include "gc.h" + +static unsigned int +hashfunc(char *s) +{ + unsigned int h = 0; + while (*s) { + if (h & 0x80000000) { + h <<= 1; + h |= 1; + } + else + h <<= 1; + h += *s; + s++; + } + return h; +} + +#define keycomp(x,y) !strcmp(x,y) + +defhashfunc(char *, int, si) +defhashfunc(char *, char *, ss) +defhashfunc(char *, void *, hist) @@ -0,0 +1,82 @@ +#ifndef HASH_H +#define HASH_H + +/* hash table */ + +#define defhash(keytype,type,sym) \ +typedef struct HashItem_##sym { \ + keytype key; \ + type value; \ + struct HashItem_##sym *next; \ +} HashItem_##sym; \ +typedef struct Hash_##sym { \ + int size; \ + struct HashItem_##sym **tab; \ +} Hash_##sym; \ +extern Hash_##sym *newHash_##sym(int size); \ +extern void putHash_##sym(Hash_##sym *t, keytype key, type value); \ +extern type getHash_##sym(Hash_##sym *t, keytype key, type failval); + +defhash(char *, int, si) +defhash(char *, char *, ss) +defhash(char *, void *, hist) +#define defhashfunc(keytype,type,sym) \ +Hash_##sym * \ +newHash_##sym(int size)\ +{\ + struct Hash_##sym *hash;\ + int i;\ +\ + hash = (Hash_##sym*)GC_malloc(sizeof(Hash_##sym));\ + hash->size = size;\ + hash->tab = (HashItem_##sym**)GC_malloc(size*sizeof(HashItem_##sym*));\ + for (i = 0; i < size; i++)\ + hash->tab[i] = NULL;\ + return hash;\ +}\ +\ +static HashItem_##sym* \ +lookupHash_##sym(Hash_##sym *t, keytype key, int *hashval_return)\ +{\ + HashItem_##sym *hi;\ +\ + *hashval_return = hashfunc(key)%t->size;\ + for (hi = t->tab[*hashval_return]; hi != NULL; hi = hi->next) {\ + if (keycomp(hi->key,key))\ + return hi;\ + }\ + return NULL;\ +}\ +\ +void \ +putHash_##sym(Hash_##sym *t, keytype key, type value)\ +{\ + int h;\ + HashItem_##sym *hi;\ +\ + hi = lookupHash_##sym(t,key,&h);\ + if (hi) {\ + hi->value = value;\ + return;\ + }\ +\ + hi = (HashItem_##sym*)GC_malloc(sizeof(HashItem_##sym));\ + hi->key = key;\ + hi->value = value;\ + hi->next = t->tab[h];\ + t->tab[h] = hi;\ +}\ +\ +type \ +getHash_##sym(Hash_##sym *t, keytype key, type failval)\ +{\ + int h;\ + HashItem_##sym *hi;\ +\ + hi = lookupHash_##sym(t,key,&h);\ + if (hi == NULL)\ + return failval;\ + return hi->value;\ +} + +#endif /* not HASH_H */ diff --git a/history.c b/history.c new file mode 100644 index 0000000..73c59a6 --- /dev/null +++ b/history.c @@ -0,0 +1,196 @@ + +#include "fm.h" + +#ifdef USE_HISTORY +Buffer * +historyBuffer(Hist * hist) +{ + Str src = Strnew(); + HistItem *item; + char *q; + + Strcat_charp(src, "<html>\n<head><title>History Page</title></head>\n"); + Strcat_charp(src, "<body>\n<h1>History Page</h1>\n<hr>\n"); + Strcat_charp(src, "<ol>\n"); + if (hist && hist->list) { + for (item = hist->list->last; item; item = item->prev) { + q = htmlquote_str((char *)item->ptr); + Strcat_charp(src, "<li><a href=\""); + Strcat_charp(src, q); + Strcat_charp(src, "\">"); + Strcat_charp(src, q); + Strcat_charp(src, "</a>\n"); + } + } + Strcat_charp(src, "</ol>\n</body>\n</html>"); + return loadHTMLString(src); +} + +void +loadHistory(Hist * hist) +{ + FILE *f; + Str line; + + if (hist == NULL) + return; + if ((f = fopen(rcFile(HISTORY_FILE), "rt")) == NULL) + return; + + while (!feof(f)) { + line = Strfgets(f); + Strchop(line); + Strremovefirstspaces(line); + Strremovetrailingspaces(line); + if (line->length == 0) + continue; + pushHist(hist, line->ptr); + } + fclose(f); +} + +void +saveHistory(Hist * hist, size_t size) +{ + FILE *f; + HistItem *item; + + if (hist == NULL || hist->list == NULL) + return; + if ((f = fopen(rcFile(HISTORY_FILE), "w")) == NULL) { + disp_err_message("Can't open history", FALSE); + return; + } + for (item = hist->list->first; item && hist->list->nitem > size; + item = item->next) + size++; + for (; item; item = item->next) + fprintf(f, "%s\n", (char *)item->ptr); + fclose(f); +} +#endif /* USE_HISTORY */ + +Hist * +newHist() +{ + Hist *hist; + + hist = New(Hist); + hist->list = (HistList *)newGeneralList(); + hist->current = NULL; + hist->hash = NULL; + return hist; +} + +HistItem * +unshiftHist(Hist *hist, char *ptr) +{ + HistItem *item; + + if (hist == NULL || hist->list == NULL) + return NULL; + item = (HistItem *)newListItem((void *)allocStr(ptr, 0), + (ListItem *)hist->list->first, NULL); + if (hist->list->first) + hist->list->first->prev = item; + else + hist->list->last = item; + hist->list->first = item; + hist->list->nitem++; + return item; +} + +HistItem * +pushHist(Hist *hist, char *ptr) + { + HistItem *item; + + if (hist == NULL || hist->list == NULL) + return NULL; + item = (HistItem *)newListItem((void *)allocStr(ptr, 0), + NULL, (ListItem *)hist->list->last); + if (hist->list->last) + hist->list->last->next = item; + else + hist->list->first = item; + hist->list->last = item; + hist->list->nitem++; + return item; +} + +/* Don't mix pushHashHist() and pushHist()/unshiftHist(). */ + +HistItem * +pushHashHist(Hist * hist, char *ptr) +{ + HistItem *item; + + if (hist == NULL || hist->list == NULL) + return NULL; + item = getHashHist(hist, ptr); + if (item) { + if (item->next) + item->next->prev = item->prev; + else /* item == hist->list->last */ + hist->list->last = item->prev; + if (item->prev) + item->prev->next = item->next; + else /* item == hist->list->first */ + hist->list->first = item->next; + hist->list->nitem--; + } + item = pushHist(hist, ptr); + putHash_hist(hist->hash, ptr, (void *)item); + return item; +} + +HistItem * +getHashHist(Hist * hist, char *ptr) +{ + HistItem *item; + + if (hist == NULL || hist->list == NULL) + return NULL; + if (hist->hash == NULL) { + hist->hash = newHash_hist(HIST_HASH_SIZE); + for (item = hist->list->first; item; item = item->next) + putHash_hist(hist->hash, (char *)item->ptr, (void *)item); + } + return (HistItem *)getHash_hist(hist->hash, ptr, NULL); +} + +char * +lastHist(Hist * hist) +{ + if (hist == NULL || hist->list == NULL) + return NULL; + if (hist->list->last) { + hist->current = hist->list->last; + return (char *)hist->current->ptr; + } + return NULL; +} + +char * +nextHist(Hist * hist) +{ + if (hist == NULL || hist->list == NULL) + return NULL; + if (hist->current && hist->current->next) { + hist->current = hist->current->next; + return (char *)hist->current->ptr; + } + return NULL; +} + +char * +prevHist(Hist * hist) +{ + if (hist == NULL || hist->list == NULL) + return NULL; + if (hist->current && hist->current->prev) { + hist->current = hist->current->prev; + return (char *)hist->current->ptr; + } + return NULL; +} diff --git a/history.h b/history.h new file mode 100644 index 0000000..034c708 --- /dev/null +++ b/history.h @@ -0,0 +1,29 @@ + +#ifndef HISTORY_H +#define HISTORY_H + +#include "textlist.h" +#include "hash.h" + +#define HIST_HASH_SIZE 127 + +typedef ListItem HistItem; + +typedef GeneralList HistList; + +typedef struct { + HistList *list; + HistItem *current; + Hash_hist *hash; +} Hist; + +extern Hist *newHist(); +extern HistItem *unshiftHist(Hist *hist, char *ptr); +extern HistItem *pushHist(Hist *hist, char *ptr); +extern HistItem *pushHashHist(Hist *hist, char *ptr); +extern HistItem *getHashHist(Hist *hist, char *ptr); +extern char *lastHist(Hist *hist); +extern char *nextHist(Hist *hist); +extern char *prevHist(Hist *hist); + +#endif /* HISTORY_H */ @@ -0,0 +1,284 @@ +#include "html.h" + +/* Define HTML Tag Infomation Table */ + +#define ATTR_CORE ATTR_ID +#define MAXA_CORE 1 +unsigned char ALST_A[] = +{ATTR_NAME,ATTR_HREF,ATTR_TARGET,ATTR_HSEQ,ATTR_REFERER,ATTR_FRAMENAME,ATTR_CORE}; +#define MAXA_A MAXA_CORE + 6 +unsigned char ALST_P[] = {ATTR_ALIGN,ATTR_CORE}; +#define MAXA_P MAXA_CORE + 1 +unsigned char ALST_UL[] = {ATTR_START,ATTR_TYPE,ATTR_CORE}; +#define MAXA_UL MAXA_CORE + 2 +unsigned char ALST_LI[] = {ATTR_TYPE,ATTR_VALUE,ATTR_CORE}; +#define MAXA_LI MAXA_CORE + 2 +unsigned char ALST_HR[] = {ATTR_WIDTH,ATTR_ALIGN,ATTR_CORE}; +#define MAXA_HR MAXA_CORE + 2 +unsigned char ALST_DL[] = {ATTR_COMPACT,ATTR_CORE}; +#define MAXA_DL MAXA_CORE + 1 +unsigned char ALST_PRE[] = {ATTR_FOR_TABLE,ATTR_CORE}; +#define MAXA_PRE MAXA_CORE + 1 +unsigned char ALST_IMG[] = +{ATTR_SRC,ATTR_ALT,ATTR_WIDTH,ATTR_HEIGHT,ATTR_USEMAP,ATTR_CORE}; +#define MAXA_IMG MAXA_CORE + 5 +unsigned char ALST_TABLE[] = +{ATTR_BORDER,ATTR_WIDTH,ATTR_HBORDER,ATTR_CELLSPACING,ATTR_CELLPADDING,ATTR_VSPACE,ATTR_CORE}; +#define MAXA_TABLE MAXA_CORE + 6 +unsigned char ALST_META[] = {ATTR_HTTP_EQUIV,ATTR_CONTENT,ATTR_CORE}; +#define MAXA_META MAXA_CORE + 2 +unsigned char ALST_FRAME[] = {ATTR_SRC,ATTR_NAME,ATTR_CORE}; +#define MAXA_FRAME MAXA_CORE + 2 +unsigned char ALST_FRAMESET[] = {ATTR_COLS,ATTR_ROWS,ATTR_CORE}; +#define MAXA_FRAMESET MAXA_CORE + 2 +unsigned char ALST_FORM[] = +{ATTR_METHOD,ATTR_ACTION,ATTR_CHARSET,ATTR_ACCEPT_CHARSET,ATTR_ENCTYPE,ATTR_TARGET,ATTR_CORE}; +#define MAXA_FORM MAXA_CORE + 6 +unsigned char ALST_INPUT[] = +{ATTR_TYPE,ATTR_VALUE,ATTR_NAME,ATTR_CHECKED,ATTR_ACCEPT,ATTR_SIZE,ATTR_MAXLENGTH,ATTR_ALT,ATTR_CORE}; +#define MAXA_INPUT MAXA_CORE + 8 +unsigned char ALST_TEXTAREA[] = {ATTR_COLS,ATTR_ROWS,ATTR_NAME,ATTR_CORE}; +#define MAXA_TEXTAREA MAXA_CORE + 3 +unsigned char ALST_SELECT[] = {ATTR_NAME,ATTR_MULTIPLE,ATTR_CORE}; +#define MAXA_SELECT MAXA_CORE + 2 +unsigned char ALST_OPTION[] = {ATTR_VALUE,ATTR_LABEL,ATTR_SELECTED,ATTR_CORE}; +#define MAXA_OPTION MAXA_CORE + 3 +unsigned char ALST_ISINDEX[] = {ATTR_ACTION,ATTR_PROMPT,ATTR_CORE}; +#define MAXA_ISINDEX MAXA_CORE + 2 +unsigned char ALST_MAP[] = {ATTR_NAME,ATTR_CORE}; +#define MAXA_MAP MAXA_CORE + 1 +unsigned char ALST_AREA[] = {ATTR_HREF,ATTR_ALT,ATTR_CORE}; +#define MAXA_AREA MAXA_CORE + 2 +unsigned char ALST_BASE[] = {ATTR_HREF,ATTR_TARGET,ATTR_CORE}; +#define MAXA_BASE MAXA_CORE + 2 +unsigned char ALST_BODY[] = {ATTR_BACKGROUND,ATTR_CORE}; +#define MAXA_BODY MAXA_CORE + 1 +unsigned char ALST_TR[] = {ATTR_ALIGN,ATTR_VALIGN,ATTR_CORE}; +#define MAXA_TR MAXA_CORE + 2 +unsigned char ALST_TD[] = +{ATTR_COLSPAN,ATTR_ROWSPAN,ATTR_ALIGN,ATTR_VALIGN,ATTR_WIDTH,ATTR_NOWRAP,ATTR_CORE}; +#define MAXA_TD MAXA_CORE + 6 +unsigned char ALST_BGSOUND[] = {ATTR_SRC,ATTR_CORE}; +#define MAX_BGSOUND MAXA_CORE + 1 +unsigned char ALST_APPLET[] = {ATTR_ARCHIVE,ATTR_CORE}; +#define MAX_APPLET MAXA_CORE + 1 +unsigned char ALST_EMBED[] = {ATTR_SRC,ATTR_CORE}; +#define MAX_EMBED MAXA_CORE + 1 + +unsigned char ALST_TABLE_ALT[] = {ATTR_TID}; +#define MAXA_TABLE_ALT 1 +unsigned char ALST_TITLE_ALT[] = {ATTR_TITLE}; +#define MAXA_TITLE_ALT 1 +unsigned char ALST_INPUT_ALT[] = +{ATTR_HSEQ,ATTR_FID,ATTR_NO_EFFECT,ATTR_TYPE,ATTR_NAME,ATTR_VALUE,ATTR_CHECKED,ATTR_ACCEPT,ATTR_SIZE,ATTR_MAXLENGTH,ATTR_TEXTAREANUMBER,ATTR_SELECTNUMBER,ATTR_ROWS}; +#define MAXA_INPUT_ALT 13 +unsigned char ALST_IMG_ALT[] = {ATTR_SRC}; +#define MAXA_IMG_ALT 1 +unsigned char ALST_NOP[] = {ATTR_CORE}; +#define MAXA_NOP MAXA_CORE + +TagInfo TagMAP[MAX_HTMLTAG] = +{ + {NULL, 0, 0}, /* 0 HTML_UNKNOWN */ + {ALST_A, MAXA_A, 0}, /* 1 HTML_A */ + {NULL, 0, TFLG_END}, /* 2 HTML_N_A */ + {ALST_P, MAXA_P, 0}, /* 3 HTML_H */ + {NULL, 0, TFLG_END}, /* 4 HTML_N_H */ + {ALST_P, MAXA_P, 0}, /* 5 HTML_P */ + {NULL, 0, 0}, /* 6 HTML_BR */ + {NULL, 0, 0}, /* 7 HTML_B */ + {NULL, 0, TFLG_END}, /* 8 HTML_N_B */ + {ALST_UL, MAXA_UL, 0}, /* 9 HTML_UL */ + {NULL, 0, TFLG_END}, /* 10 HTML_N_UL */ + {ALST_LI, MAXA_LI, 0}, /* 11 HTML_LI */ + {ALST_UL, MAXA_UL, 0}, /* 12 HTML_OL */ + {NULL, 0, TFLG_END}, /* 13 HTML_N_OL */ + {NULL, 0, 0}, /* 14 HTML_TITLE */ + {NULL, 0, TFLG_END}, /* 15 HTML_N_TITLE */ + {ALST_HR, MAXA_HR, 0}, /* 16 HTML_HR */ + {ALST_DL, MAXA_DL, 0}, /* 17 HTML_DL */ + {NULL, 0, TFLG_END}, /* 18 HTML_N_DL */ + {NULL, 0, 0}, /* 19 HTML_DT */ + {NULL, 0, 0}, /* 20 HTML_DD */ + {ALST_PRE, MAXA_PRE, 0}, /* 21 HTML_PRE */ + {NULL, 0, TFLG_END}, /* 22 HTML_N_PRE */ + {NULL, 0, 0}, /* 23 HTML_BLQ */ + {NULL, 0, TFLG_END}, /* 24 HTML_N_BLQ */ + {ALST_IMG, MAXA_IMG, 0}, /* 25 HTML_IMG */ + {NULL, 0, 0}, /* 26 HTML_LISTING */ + {NULL, 0, TFLG_END}, /* 27 HTML_N_LISTING */ + {NULL, 0, 0}, /* 28 HTML_XMP */ + {NULL, 0, TFLG_END}, /* 29 HTML_N_XMP */ + {NULL, 0, 0}, /* 30 HTML_PLAINTEXT */ + {ALST_TABLE, MAXA_TABLE, 0}, /* 31 HTML_TABLE */ + {NULL, 0, TFLG_END}, /* 32 HTML_N_TABLE */ + {ALST_META, MAXA_META, 0}, /* 33 HTML_META */ + {NULL, 0, TFLG_END}, /* 34 HTML_N_P */ + {ALST_FRAME, MAXA_FRAME, 0}, /* 35 HTML_FRAME */ + {ALST_FRAMESET, MAXA_FRAMESET, 0}, /* 36 HTML_FRAMESET */ + {NULL, 0, TFLG_END}, /* 37 HTML_N_FRAMESET */ + {NULL, 0, 0}, /* 38 HTML_CENTER */ + {NULL, 0, TFLG_END}, /* 39 HTML_N_CENTER */ + {NULL, 0, 0}, /* 40 HTML_FONT */ + {NULL, 0, TFLG_END}, /* 41 HTML_N_FONT */ + {ALST_FORM, MAXA_FORM, 0}, /* 42 HTML_FORM */ + {NULL, 0, TFLG_END}, /* 43 HTML_N_FORM */ + {ALST_INPUT, MAXA_INPUT, 0}, /* 44 HTML_INPUT */ + {ALST_TEXTAREA, MAXA_TEXTAREA, 0}, /* 45 HTML_TEXTAREA */ + {NULL, 0, TFLG_END}, /* 46 HTML_N_TEXTAREA */ + {ALST_SELECT, MAXA_SELECT, 0}, /* 47 HTML_SELECT */ + {NULL, 0, TFLG_END}, /* 48 HTML_N_SELECT */ + {ALST_OPTION, MAXA_OPTION, 0}, /* 49 HTML_OPTION */ + {NULL, 0, 0}, /* 50 HTML_NOBR */ + {NULL, 0, TFLG_END}, /* 51 HTML_N_NOBR */ + {ALST_P, MAXA_P, 0}, /* 52 HTML_DIV */ + {NULL, 0, TFLG_END}, /* 53 HTML_N_DIV */ + {ALST_ISINDEX, MAXA_ISINDEX, 0}, /* 54 HTML_ISINDEX */ + {ALST_MAP, MAXA_MAP, 0}, /* 55 HTML_MAP */ + {NULL, 0, TFLG_END}, /* 56 HTML_N_MAP */ + {ALST_AREA, MAXA_AREA, 0}, /* 57 HTML_AREA */ + {NULL, 0, 0}, /* 58 HTML_SCRIPT */ + {NULL, 0, TFLG_END}, /* 59 HTML_N_SCRIPT */ + {ALST_BASE, MAXA_BASE, 0}, /* 60 HTML_BASE */ + {NULL, 0, 0}, /* 61 HTML_DEL */ + {NULL, 0, TFLG_END}, /* 62 HTML_N_DEL */ + {NULL, 0, 0}, /* 63 HTML_INS */ + {NULL, 0, TFLG_END}, /* 64 HTML_N_INS */ + {NULL, 0, 0}, /* 65 HTML_U */ + {NULL, 0, TFLG_END}, /* 66 HTML_N_U */ + {NULL, 0, 0}, /* 67 HTML_STYLE */ + {NULL, 0, TFLG_END}, /* 68 HTML_N_STYLE */ + {NULL, 0, 0}, /* 69 HTML_WBR */ + {NULL, 0, 0}, /* 70 HTML_EM */ + {NULL, 0, TFLG_END}, /* 71 HTML_N_EM */ + {ALST_BODY, MAXA_BODY, 0}, /* 72 HTML_BODY */ + {NULL, 0, TFLG_END}, /* 73 HTML_N_BODY */ + {ALST_TR, MAXA_TR, 0}, /* 74 HTML_TR */ + {NULL, 0, TFLG_END}, /* 75 HTML_N_TR */ + {ALST_TD, MAXA_TD, 0}, /* 76 HTML_TD */ + {NULL, 0, TFLG_END}, /* 77 HTML_N_TD */ + {NULL, 0, 0}, /* 78 HTML_CAPTION */ + {NULL, 0, TFLG_END}, /* 79 HTML_N_CAPTION */ + {ALST_TD, MAXA_TD, 0}, /* 80 HTML_TH */ + {NULL, 0, TFLG_END}, /* 81 HTML_N_TH */ + {NULL, 0, 0}, /* 82 HTML_THEAD */ + {NULL, 0, TFLG_END}, /* 83 HTML_N_THEAD */ + {NULL, 0, 0}, /* 84 HTML_TBODY */ + {NULL, 0, TFLG_END}, /* 85 HTML_N_TBODY */ + {NULL, 0, 0}, /* 86 HTML_TFOOT */ + {NULL, 0, TFLG_END}, /* 87 HTML_N_TFOOT */ + {NULL, 0, 0}, /* 88 HTML_COLGROUP */ + {NULL, 0, TFLG_END}, /* 89 HTML_N_COLGROUP */ + {NULL, 0, 0}, /* 90 HTML_COL */ + {ALST_BGSOUND, MAX_BGSOUND, 0}, /* 91 HTML_BGSOUND */ + {ALST_APPLET, MAX_APPLET, 0}, /* 92 HTML_APPLET */ + {ALST_EMBED, MAX_EMBED, 0}, /* 93 HTML_EMBED */ + {NULL, 0, TFLG_END}, /* 94 HTML_N_OPTION */ + {NULL, 0, 0}, /* 95 HTML_HEAD */ + {NULL, 0, TFLG_END}, /* 96 HTML_N_HEAD */ + {NULL, 0, 0}, /* 97 HTML_DOCTYPE */ + + {NULL, 0, 0}, /* 98 Undefined */ + {NULL, 0, 0}, /* 99 Undefined */ + {NULL, 0, 0}, /* 100 Undefined */ + {NULL, 0, 0}, /* 101 Undefined */ + {NULL, 0, 0}, /* 102 Undefined */ + {NULL, 0, 0}, /* 103 Undefined */ + {NULL, 0, 0}, /* 104 Undefined */ + {NULL, 0, 0}, /* 105 Undefined */ + {NULL, 0, 0}, /* 106 Undefined */ + {NULL, 0, 0}, /* 107 Undefined */ + {NULL, 0, 0}, /* 108 Undefined */ + {NULL, 0, 0}, /* 109 Undefined */ + {NULL, 0, 0}, /* 110 Undefined */ + {NULL, 0, 0}, /* 111 Undefined */ + {NULL, 0, 0}, /* 112 Undefined */ + + /* pseudo tag */ + {ALST_TABLE_ALT,MAXA_TABLE_ALT,TFLG_INT}, /* 113 HTML_TABLE_ALT */ + {NULL, 0, TFLG_INT}, /* 114 HTML_RULE */ + {NULL, 0, TFLG_INT|TFLG_END}, /* 115 HTML_N_RULE */ + {NULL, 0, TFLG_INT}, /* 116 HTML_PRE_INT */ + {NULL, 0, TFLG_INT|TFLG_END}, /* 117 HTML_N_PRE_INT */ + {ALST_TITLE_ALT,MAXA_TITLE_ALT,TFLG_INT}, /* 118 HTML_TITLE_ALT */ + {ALST_FORM, MAXA_FORM, TFLG_INT}, /* 119 HTML_FORM_INT */ + {NULL, 0, TFLG_INT|TFLG_END}, /* 120 HTML_N_FORM_INT */ + {NULL, 0, TFLG_INT}, /* 121 HTML_DL_COMPACT */ + {ALST_INPUT_ALT,MAXA_INPUT_ALT,TFLG_INT}, /* 122 HTML_INPUT_ALT */ + {NULL, 0, TFLG_INT|TFLG_END}, /* 123 HTML_N_INPUT_ALT */ + {ALST_IMG_ALT, MAXA_IMG_ALT, TFLG_INT}, /* 124 HTML_IMG_ALT */ + {NULL, 0, TFLG_INT|TFLG_END}, /* 125 HTML_N_IMG_ALT */ + {NULL, 0, TFLG_INT}, /* 126 HTML_EOL */ + {ALST_NOP, MAXA_NOP, TFLG_INT}, /* 127 HTML_NOP */ +}; + +TagAttrInfo AttrMAP[MAX_TAGATTR] = +{ + {NULL , VTYPE_NONE, 0}, /* 0 ATTR_UNKNOWN */ + {"accept" , VTYPE_NONE, 0}, /* 1 ATTR_ACCEPT */ + {"accept-charset", VTYPE_STR, 0}, /* 2 ATTR_ACCEPT_CHARSET */ + {"action" , VTYPE_ACTION, 0}, /* 3 ATTR_ACTION */ + {"align" , VTYPE_ALIGN, 0}, /* 4 ATTR_ALIGN */ + {"alt" , VTYPE_STR, 0}, /* 5 ATTR_ALT */ + {"archive" , VTYPE_STR, 0}, /* 6 ATTR_ARCHIVE */ + {"background" , VTYPE_STR, 0}, /* 7 ATTR_BACKGROUND */ + {"border" , VTYPE_NUMBER, 0}, /* 8 ATTR_BORDER */ + {"cellpadding" , VTYPE_NUMBER, 0}, /* 9 ATTR_CELLPADDING */ + {"cellspacing" , VTYPE_NUMBER, 0}, /* 10 ATTR_CELLSPACING */ + {"charset" , VTYPE_STR, 0}, /* 11 ATTR_CHARSET */ + {"checked" , VTYPE_NONE, 0}, /* 12 ATTR_CHECKED */ + {"cols" , VTYPE_MLENGTH, 0}, /* 13 ATTR_COLS */ + {"colspan" , VTYPE_NUMBER, 0}, /* 14 ATTR_COLSPAN */ + {"content" , VTYPE_STR, 0}, /* 15 ATTR_CONTENT */ + {"enctype" , VTYPE_ENCTYPE, 0}, /* 16 ATTR_ENCTYPE */ + {"height" , VTYPE_LENGTH, 0}, /* 17 ATTR_HEIGHT */ + {"href" , VTYPE_STR, 0}, /* 18 ATTR_HREF */ + {"http-equiv" , VTYPE_STR, 0}, /* 19 ATTR_HTTP_EQUIV */ + {"id" , VTYPE_STR, 0}, /* 20 ATTR_ID */ + {"link" , VTYPE_STR, 0}, /* 21 ATTR_LINK */ + {"maxlength" , VTYPE_NUMBER, 0}, /* 22 ATTR_MAXLENGTH */ + {"method" , VTYPE_METHOD, 0}, /* 23 ATTR_METHOD */ + {"multiple" , VTYPE_NONE, 0}, /* 24 ATTR_MULTIPLE */ + {"name" , VTYPE_STR, 0}, /* 25 ATTR_NAME */ + {"nowrap" , VTYPE_NONE, 0}, /* 26 ATTR_NOWRAP */ + {"prompt" , VTYPE_STR, 0}, /* 27 ATTR_PROMPT */ + {"rows" , VTYPE_MLENGTH, 0}, /* 28 ATTR_ROWS */ + {"rowspan" , VTYPE_NUMBER, 0}, /* 29 ATTR_ROWSPAN */ + {"size" , VTYPE_NUMBER, 0}, /* 30 ATTR_SIZE */ + {"src" , VTYPE_STR, 0}, /* 31 ATTR_SRC */ + {"target" , VTYPE_STR, 0}, /* 32 ATTR_TARGET */ + {"type" , VTYPE_TYPE, 0}, /* 33 ATTR_TYPE */ + {"usemap" , VTYPE_STR, 0}, /* 34 ATTR_USEMAP */ + {"valign" , VTYPE_VALIGN, 0}, /* 35 ATTR_VALIGN */ + {"value" , VTYPE_STR, 0}, /* 36 ATTR_VALUE */ + {"vspace" , VTYPE_NUMBER, 0}, /* 37 ATTR_VSPACE */ + {"width" , VTYPE_LENGTH, 0}, /* 38 ATTR_WIDTH */ + {"compact" , VTYPE_NONE, 0}, /* 39 ATTR_COMPACT */ + {"start" , VTYPE_NUMBER, 0}, /* 40 ATTR_START */ + {"selected" , VTYPE_NONE, 0}, /* 41 ATTR_SELECTED */ + {"label" , VTYPE_STR, 0}, /* 42 ATTR_LABEL */ + + {NULL , VTYPE_NONE, 0}, /* 43 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 44 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 45 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 46 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 47 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 48 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 49 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 50 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 51 Undefined */ + {NULL , VTYPE_NONE, 0}, /* 52 Undefined */ + + /* Internal attribute */ + {"tid" , VTYPE_NUMBER, AFLG_INT}, /* 53 ATTR_TID */ + {"fid" , VTYPE_NUMBER, AFLG_INT}, /* 54 ATTR_FID */ + {"for_table" , VTYPE_NONE, AFLG_INT}, /* 55 ATTR_FOR_TABLE */ + {"framename" , VTYPE_STR, AFLG_INT}, /* 56 ATTR_FRAMENAME */ + {"hborder" , VTYPE_NONE, 0}, /* 57 ATTR_HBORDER */ + {"hseq" , VTYPE_NUMBER, AFLG_INT}, /* 58 ATTR_HSEQ */ + {"no_effect" , VTYPE_NONE, AFLG_INT}, /* 59 ATTR_NO_EFFECT */ + {"referer" , VTYPE_STR, AFLG_INT}, /* 60 ATTR_REFERER */ + {"selectnumber" , VTYPE_NUMBER, AFLG_INT}, /* 61 ATTR_SELECTNUMBER */ + {"textareanumber", VTYPE_NUMBER, AFLG_INT}, /* 62 ATTR_TEXTAREANUMBER */ + {"title" , VTYPE_STR, AFLG_INT}, /* 63 ATTR_TITLE */ +}; @@ -0,0 +1,325 @@ +/* $Id: html.h,v 1.1 2001/11/08 05:15:00 a-ito Exp $ */ +#ifndef _HTML_H +#define _HTML_H +#ifdef USE_SSL +#include <bio.h> +#include <x509.h> +#include <ssl.h> +#endif /* USE_SSL */ + +#include "istream.h" + +#define StrUFgets(f) StrISgets((f)->stream) +#define StrmyUFgets(f) StrmyISgets((f)->stream) +#define UFgetc(f) ISgetc((f)->stream) +#define UFundogetc(f) ISundogetc((f)->stream) +#define UFread(f,buf,len) ISread((f)->stream,buf,len) +#define UFclose(f) (ISclose((f)->stream), (f)->stream = NULL) + +struct cmdtable { + char *cmdname; + int cmd; +}; + +struct mailcap { + char *type; + char *viewer; + int flags; + char *test; + char *nametemplate; + char *edit; +}; + +#define MAILCAP_NEEDSTERMINAL 0x01 +#define MAILCAP_COPIOUSOUTPUT 0x02 +#define MAILCAP_HTMLOUTPUT 0x04 + +#define MCSTAT_REPNAME 0x01 +#define MCSTAT_REPTYPE 0x02 + +struct table2 { + char *item1; + char *item2; +}; + +typedef struct { + char *referer; + int flag; +} URLOption; + +typedef struct _ParsedURL { + int scheme; + char *user; + char *pass; + char *host; + int port; + char *file; + char *label; + int is_nocache; +} ParsedURL; + +typedef struct { + unsigned char scheme; + char is_cgi; + char encoding; + InputStream stream; + char *ext; + int compression; + char *guess_type; +} URLFile; + +#define CMP_NOCOMPRESS 0 +#define CMP_COMPRESS 1 +#define CMP_GZIP 2 +#define CMP_BZIP2 3 +#define CMP_DEFLATE 4 + +#define ENC_7BIT 0 +#define ENC_BASE64 1 +#define ENC_QUOTE 2 + +#define HTML_UNKNOWN 0 +#define HTML_A 1 +#define HTML_N_A 2 +#define HTML_H 3 +#define HTML_N_H 4 +#define HTML_P 5 +#define HTML_BR 6 +#define HTML_B 7 +#define HTML_N_B 8 +#define HTML_UL 9 +#define HTML_N_UL 10 +#define HTML_LI 11 +#define HTML_OL 12 +#define HTML_N_OL 13 +#define HTML_TITLE 14 +#define HTML_N_TITLE 15 +#define HTML_HR 16 +#define HTML_DL 17 +#define HTML_N_DL 18 +#define HTML_DT 19 +#define HTML_DD 20 +#define HTML_PRE 21 +#define HTML_N_PRE 22 +#define HTML_BLQ 23 +#define HTML_N_BLQ 24 +#define HTML_IMG 25 +#define HTML_LISTING 26 +#define HTML_N_LISTING 27 +#define HTML_XMP 28 +#define HTML_N_XMP 29 +#define HTML_PLAINTEXT 30 +#define HTML_TABLE 31 +#define HTML_N_TABLE 32 +#define HTML_META 33 +#define HTML_N_P 34 +#define HTML_FRAME 35 +#define HTML_FRAMESET 36 +#define HTML_N_FRAMESET 37 +#define HTML_CENTER 38 +#define HTML_N_CENTER 39 +#define HTML_FONT 40 +#define HTML_N_FONT 41 +#define HTML_FORM 42 +#define HTML_N_FORM 43 +#define HTML_INPUT 44 +#define HTML_TEXTAREA 45 +#define HTML_N_TEXTAREA 46 +#define HTML_SELECT 47 +#define HTML_N_SELECT 48 +#define HTML_OPTION 49 +#define HTML_NOBR 50 +#define HTML_N_NOBR 51 +#define HTML_DIV 52 +#define HTML_N_DIV 53 +#define HTML_ISINDEX 54 +#define HTML_MAP 55 +#define HTML_N_MAP 56 +#define HTML_AREA 57 +#define HTML_SCRIPT 58 +#define HTML_N_SCRIPT 59 +#define HTML_BASE 60 +#define HTML_DEL 61 +#define HTML_N_DEL 62 +#define HTML_INS 63 +#define HTML_N_INS 64 +#define HTML_U 65 +#define HTML_N_U 66 +#define HTML_STYLE 67 +#define HTML_N_STYLE 68 +#define HTML_WBR 69 +#define HTML_EM 70 +#define HTML_N_EM 71 +#define HTML_BODY 72 +#define HTML_N_BODY 73 +#define HTML_TR 74 +#define HTML_N_TR 75 +#define HTML_TD 76 +#define HTML_N_TD 77 +#define HTML_CAPTION 78 +#define HTML_N_CAPTION 79 +#define HTML_TH 80 +#define HTML_N_TH 81 +#define HTML_THEAD 82 +#define HTML_N_THEAD 83 +#define HTML_TBODY 84 +#define HTML_N_TBODY 85 +#define HTML_TFOOT 86 +#define HTML_N_TFOOT 87 +#define HTML_COLGROUP 88 +#define HTML_N_COLGROUP 89 +#define HTML_COL 90 +#define HTML_BGSOUND 91 +#define HTML_APPLET 92 +#define HTML_EMBED 93 +#define HTML_N_OPTION 94 +#define HTML_HEAD 95 +#define HTML_N_HEAD 96 +#define HTML_DOCTYPE 97 + + + /* pseudo tag */ +#define HTML_TABLE_ALT 113 +#define HTML_RULE 114 +#define HTML_N_RULE 115 +#define HTML_PRE_INT 116 +#define HTML_N_PRE_INT 117 +#define HTML_TITLE_ALT 118 +#define HTML_FORM_INT 119 +#define HTML_N_FORM_INT 120 +#define HTML_DL_COMPACT 121 +#define HTML_INPUT_ALT 122 +#define HTML_N_INPUT_ALT 123 +#define HTML_IMG_ALT 124 +#define HTML_N_IMG_ALT 125 +#define HTML_EOL 126 +#define HTML_NOP 127 + +#define MAX_HTMLTAG 128 + +/* Tag attribute */ + +#define ATTR_UNKNOWN 0 +#define ATTR_ACCEPT 1 +#define ATTR_ACCEPT_CHARSET 2 +#define ATTR_ACTION 3 +#define ATTR_ALIGN 4 +#define ATTR_ALT 5 +#define ATTR_ARCHIVE 6 +#define ATTR_BACKGROUND 7 +#define ATTR_BORDER 8 +#define ATTR_CELLPADDING 9 +#define ATTR_CELLSPACING 10 +#define ATTR_CHARSET 11 +#define ATTR_CHECKED 12 +#define ATTR_COLS 13 +#define ATTR_COLSPAN 14 +#define ATTR_CONTENT 15 +#define ATTR_ENCTYPE 16 +#define ATTR_HEIGHT 17 +#define ATTR_HREF 18 +#define ATTR_HTTP_EQUIV 19 +#define ATTR_ID 20 +#define ATTR_LINK 21 +#define ATTR_MAXLENGTH 22 +#define ATTR_METHOD 23 +#define ATTR_MULTIPLE 24 +#define ATTR_NAME 25 +#define ATTR_NOWRAP 26 +#define ATTR_PROMPT 27 +#define ATTR_ROWS 28 +#define ATTR_ROWSPAN 29 +#define ATTR_SIZE 30 +#define ATTR_SRC 31 +#define ATTR_TARGET 32 +#define ATTR_TYPE 33 +#define ATTR_USEMAP 34 +#define ATTR_VALIGN 35 +#define ATTR_VALUE 36 +#define ATTR_VSPACE 37 +#define ATTR_WIDTH 38 +#define ATTR_COMPACT 39 +#define ATTR_START 40 +#define ATTR_SELECTED 41 +#define ATTR_LABEL 42 + +/* Internal attribute */ +#define ATTR_TID 53 +#define ATTR_FID 54 +#define ATTR_FOR_TABLE 55 +#define ATTR_FRAMENAME 56 +#define ATTR_HBORDER 57 +#define ATTR_HSEQ 58 +#define ATTR_NO_EFFECT 59 +#define ATTR_REFERER 60 +#define ATTR_SELECTNUMBER 61 +#define ATTR_TEXTAREANUMBER 62 +#define ATTR_TITLE 63 + +#define MAX_TAGATTR 64 + +/* HTML Tag Information Table */ + +typedef struct html_tag_info { + unsigned char *accept_attribute; + unsigned char max_attribute; + unsigned char flag; +} TagInfo; + +#define TFLG_END 1 +#define TFLG_INT 2 + +/* HTML Tag Attribute Information Table */ + +typedef struct tag_attribute_info { + char *name; + unsigned char vtype; + unsigned char flag; +} TagAttrInfo; + +#define AFLG_INT 1 + +#define VTYPE_NONE 0 +#define VTYPE_STR 1 +#define VTYPE_NUMBER 2 +#define VTYPE_LENGTH 3 +#define VTYPE_ALIGN 4 +#define VTYPE_VALIGN 5 +#define VTYPE_ACTION 6 +#define VTYPE_ENCTYPE 7 +#define VTYPE_METHOD 8 +#define VTYPE_MLENGTH 9 +#define VTYPE_TYPE 10 + +extern TagInfo TagMAP[]; +extern TagAttrInfo AttrMAP[]; + +struct environment { + char env; + int type; + int count; + char indent; +}; + +#define MAX_ENV_LEVEL 20 +#define MAX_INDENT_LEVEL 10 + +#define INDENT_INCR 4 + +#define SCM_UNKNOWN 255 +#define SCM_MISSING 254 +#define SCM_HTTP 0 +#define SCM_GOPHER 1 +#define SCM_FTP 2 +#define SCM_FTPDIR 3 +#define SCM_LOCAL 4 +#define SCM_LOCAL_CGI 5 +#define SCM_EXEC 6 +#define SCM_NNTP 7 +#define SCM_NEWS 8 +#define SCM_MAILTO 9 +#ifdef USE_SSL +#define SCM_HTTPS 10 +#endif /* USE_SSL */ + +#endif /* _HTML_H */ @@ -0,0 +1,736 @@ +/* $Id: indep.c,v 1.1 2001/11/08 05:15:01 a-ito Exp $ */ +#include "fm.h" +#include <stdio.h> +#include <pwd.h> +#include <sys/param.h> +#include <sys/types.h> +#include <stdlib.h> +#include "indep.h" +#include "Str.h" +#include "gc.h" +#include "myctype.h" + +#if defined(__EMX__)&&!defined(JP_CHARSET) +int CodePage=0; +#endif + +static int is_safe(int); + +struct table3 { + char *item1; + unsigned char item2; +}; + +struct table3 escapetbl[] = +{ + {"lt", '<'}, + {"gt", '>'}, + {"amp", '&'}, + {"quot", '"'}, + {"LT", '<'}, + {"GT", '>'}, + {"AMP", '&'}, + {"QUOT", '"'}, +/* for latin1_tbl */ + {"nbsp", 128 + 32}, + {"NBSP", 128 + 32}, + {"iexcl", 128 + 33}, + {"cent", 128 + 34}, + {"pound", 128 + 35}, + {"curren", 128 + 36}, + {"yen", 128 + 37}, + {"brvbar", 128 + 38}, + {"sect", 128 + 39}, + {"uml", 128 + 40}, + {"copy", 128 + 41}, + {"ordf", 128 + 42}, + {"laquo", 128 + 43}, + {"not", 128 + 44}, + {"shy", 128 + 45}, + {"reg", 128 + 46}, + {"macr", 128 + 47}, + {"deg", 128 + 48}, + {"plusmn", 128 + 49}, + {"sup2", 128 + 50}, + {"sup3", 128 + 51}, + {"acute", 128 + 52}, + {"micro", 128 + 53}, + {"para", 128 + 54}, + {"middot", 128 + 55}, + {"cedil", 128 + 56}, + {"sup1", 128 + 57}, + {"ordm", 128 + 58}, + {"raquo", 128 + 59}, + {"frac14", 128 + 60}, + {"frac12", 128 + 61}, + {"frac34", 128 + 62}, + {"iquest", 128 + 63}, + {"Agrave", 128 + 64}, + {"Aacute", 128 + 65}, + {"Acirc", 128 + 66}, + {"Atilde", 128 + 67}, + {"Auml", 128 + 68}, + {"Aring", 128 + 69}, + {"AElig", 128 + 70}, + {"Ccedil", 128 + 71}, + {"Egrave", 128 + 72}, + {"Eacute", 128 + 73}, + {"Ecirc", 128 + 74}, + {"Euml", 128 + 75}, + {"Igrave", 128 + 76}, + {"Iacute", 128 + 77}, + {"Icirc", 128 + 78}, + {"Iuml", 128 + 79}, + {"ETH", 128 + 80}, + {"Ntilde", 128 + 81}, + {"Ograve", 128 + 82}, + {"Oacute", 128 + 83}, + {"Ocirc", 128 + 84}, + {"Otilde", 128 + 85}, + {"Ouml", 128 + 86}, + {"times", 128 + 87}, + {"Oslash", 128 + 88}, + {"Ugrave", 128 + 89}, + {"Uacute", 128 + 90}, + {"Ucirc", 128 + 91}, + {"Uuml", 128 + 92}, + {"Yacute", 128 + 93}, + {"THORN", 128 + 94}, + {"szlig", 128 + 95}, + {"agrave", 128 + 96}, + {"aacute", 128 + 97}, + {"acirc", 128 + 98}, + {"atilde", 128 + 99}, + {"auml", 128 + 100}, + {"aring", 128 + 101}, + {"aelig", 128 + 102}, + {"ccedil", 128 + 103}, + {"egrave", 128 + 104}, + {"eacute", 128 + 105}, + {"ecirc", 128 + 106}, + {"euml", 128 + 107}, + {"igrave", 128 + 108}, + {"iacute", 128 + 109}, + {"icirc", 128 + 110}, + {"iuml", 128 + 111}, + {"eth", 128 + 112}, + {"ntilde", 128 + 113}, + {"ograve", 128 + 114}, + {"oacute", 128 + 115}, + {"ocirc", 128 + 116}, + {"otilde", 128 + 117}, + {"ouml", 128 + 118}, + {"divide", 128 + 119}, + {"oslash", 128 + 120}, + {"ugrave", 128 + 121}, + {"uacute", 128 + 122}, + {"ucirc", 128 + 123}, + {"uuml", 128 + 124}, + {"yacute", 128 + 125}, + {"thorn", 128 + 126}, + {"yuml", 128 + 127}, + {NULL, 0}, +}; + +#ifdef JP_CHARSET +static char *latin1_tbl[128] = +{ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 0- 7 */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 8- 15 */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 16- 23 */ + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 24- 31 */ + NBSP, "!", "��", "��", NULL, "��", "|", "��", /* 32- 39 */ + "��", "(C)", NULL, "��", "��", "-", "(R)", "��", /* 40- 47 */ + "��", "��", "2", "3", "'", "��", "��", "��", /* 48- 55 */ + ",", "1", NULL, "��", "1/4", "1/2", "3/4", "?", /* 56- 63 */ + "A`", "A'", "A^", "A~", "Ae", "��", "AE", "C", /* 64- 71 */ + "E`", "E'", "E^", "E", "I`", "I'", "I^", "I", /* 72- 79 */ + "D", "N~", "O`", "O'", "O^", "O~", "Oe", "��", /* 80- 87 */ + "��", "U`", "U'", "U^", "Ue", "Y'", "th", "ss", /* 88- 95 */ + "a`", "a'", "a^", "a~", "ae", "a", "ae", "c", /* 96-103 */ + "e`", "e'", "e^", "e", "i`", "i'", "i^", "i", /* 104-111 */ + "dh", "n~", "o`", "o'", "o^", "o~", "oe", "��", /* 112-119 */ + "��", "u`", "u'", "u^", "ue", "y'", "th", "y" /* 120-127 */ +}; + +char * +conv_latin1(int ch) +{ + static char dummy[] = {0, 0}; + if (ch > 0xff) { + /* it must be a unicode character; w3m can't handle it */ + return "??"; + } + if (!IS_PRINT(ch) && !IS_CNTRL(ch)) { + char *save; + if (ch >= 0x80 && (save = latin1_tbl[ch - 0x80])) { + return save; + } + return "?"; + } + else { + dummy[0] = ch; + return dummy; + } +} +#else /* not JP_CHARSET */ +#ifdef __EMX__ +/* + * Character conversion table + * ( to code page 850 from iso-8859-1 ) + * + * Following character constants are in code page 850. + */ +static char latin1_tbl[96] = { + ' ', '\255', '\275', '\234', '\317', '\276', '\335', '\365', + '\371', '\270', '\246', '\256', '\252', '\360', '\251', '\356', + '\370', '\361', '\375', '\374', '\357', '\346', '\364', '\372', + '\367', '\373', '\247', '\257', '\254', '\253', '\363', '\250', + '\267', '\265', '\266', '\307', '\216', '\217', '\222', '\200', + '\324', '\220', '\322', '\323', '\336', '\326', '\327', '\330', + '\321', '\245', '\343', '\340', '\342', '\345', '\231', '\236', + '\235', '\353', '\351', '\352', '\232', '\355', '\350', '\341', + '\205', '\240', '\203', '\306', '\204', '\206', '\221', '\207', + '\212', '\202', '\210', '\211', '\215', '\241', '\214', '\213', + '\320', '\244', '\225', '\242', '\223', '\344', '\224', '\366', + '\233', '\227', '\243', '\226', '\201', '\354', '\347', '\230' +}; +#endif + +char * +conv_latin1(int ch) +{ + static char dummy[2] = {0, 0}; + if (ch > 0xff) { + /* it must be a unicode character; w3m can't handle it */ + return "??"; + } +#ifdef __EMX__ + { + if(CodePage==850&&ch>=160) + ch=latin1_tbl[(unsigned)ch-160]; + } +#endif + dummy[0] = ch; + return dummy; +} +#endif /* not JP_CHARSET */ + +int +getescapechar(char **s) +{ + int i, dummy = 0; + char *save; + Str tmp = Strnew(); + + save = *s; + if (**s == '&') + (*s)++; + if (**s == '#') { + if (*(*s + 1) == 'x') { + (*s)++; + sscanf(*s + 1, "%x", &dummy); + } + else if (*(*s + 1) == 'X') { + (*s)++; + sscanf(*s + 1, "%X", &dummy); + } + else { + sscanf(*s + 1, "%d", &dummy); + } + (*s)++; + save = *s; + while (**s && **s != ';') { + if (!IS_ALNUM(**s)) { + if (*s > save) + break; + else + goto fail; + } + (*s)++; + } + if (**s == ';') + (*s)++; + return dummy; + } + while (IS_ALNUM(**s)) { + Strcat_char(tmp, **s); + (*s)++; + } + /* if (**s != ';') goto fail; */ + if (**s == ';') + (*s)++; + for (i = 0; escapetbl[i].item1 != NULL; i++) + if (Strcmp_charp(tmp, escapetbl[i].item1) == 0) { + return escapetbl[i].item2; + } + fail: + return '\0'; +} + +char * +getescapecmd(char **s) +{ + char *save = *s; + Str tmp, tmp2; + int ch = getescapechar(s); + if (ch) + return conv_latin1(ch); + + tmp = Strnew_charp_n(save, *s - save); + if (tmp->ptr[0] != '&') { + tmp2 = Strnew_charp("&"); + Strcat(tmp2, tmp); + return tmp2->ptr; + } + return tmp->ptr; +} + +char * +allocStr(const char *s, int len) +{ + char *ptr; + + if (s == NULL) + return NULL; + if (len == 0) + len = strlen(s); + ptr = NewAtom_N(char, len + 1); + if (ptr == NULL) { + fprintf(stderr, "fm: Can't allocate string. Give me more memory!\n"); + exit(-1); + } + bcopy(s, ptr, len); + ptr[len] = '\0'; + return ptr; +} + +int +strCmp(const void *s1, const void *s2) +{ + unsigned char *p1 = *(unsigned char **) s1; + unsigned char *p2 = *(unsigned char **) s2; + + while ((*p1 != '\0') && (*p1 == *p2)) { + p1++; + p2++; + } + return (*p1 - *p2); +} + +void +copydicname(char *s, char *fn) +{ + strcpy(s, fn); + for (fn = &s[strlen(s)]; s < fn; fn--) { + if (*fn == '/') { + *(fn + 1) = '\0'; + return; + } + } + if (*fn == '/') + *(fn + 1) = '\0'; + else + *fn = '\0'; +} + +#ifndef __EMX__ +char * +currentdir() +{ + char *path; +#ifdef GETCWD + path = New_N(char, MAXPATHLEN); + getcwd(path, MAXPATHLEN); +#else /* not GETCWD */ +#ifdef GETWD + path = New_N(char, 1024); + getwd(path); +#else /* not GETWD */ + FILE *f; + char *p; + path = New_N(char, 1024); + f = popen("pwd", "r"); + fgets(path, 1024, f); + pclose(f); + for (p = path; *p; p++) + if (*p == '\n') { + *p = '\0'; + break; + } +#endif /* not GETWD */ +#endif /* not GETCWD */ + return path; +} +#endif /* __EMX__ */ + +char * +cleanupName(char *name) +{ + char *buf, *p, *q; + + buf = allocStr(name, 0); + p = buf; + q = name; + while (*q != '\0' && *q != '?') { + if (strncmp(p, "/../", 4) == 0) { /* foo/bar/../FOO */ + if (p - 2 == buf && strncmp(p - 2, "..", 2) == 0) { + /* ../../ */ + p += 3; + q += 3; + } + else if (p - 3 >= buf && strncmp(p - 3, "/..", 3) == 0) { + /* ../../../ */ + p += 3; + q += 3; + } + else { + while (p != buf && *--p != '/'); /* ->foo/FOO */ + *p = '\0'; + q += 3; + strcat(buf, q); + } + } + else if (strcmp(p, "/..") == 0) { /* foo/bar/.. */ + if (p - 2 == buf && strncmp(p - 2, "..", 2) == 0) { + /* ../.. */ + } + else if (p - 3 >= buf && strncmp(p - 3, "/..", 3) == 0) { + /* ../../.. */ + } + else { + while (p != buf && *--p != '/'); /* ->foo/ */ + *++p = '\0'; + } + break; + } + else if (strncmp(p, "/./", 3) == 0) { /* foo/./bar */ + *p = '\0'; /* -> foo/bar */ + q += 2; + strcat(buf, q); + } + else if (strcmp(p, "/.") == 0) { /* foo/. */ + *++p = '\0'; /* -> foo/ */ + break; + } + else if (strncmp(p, "//", 2) == 0) { /* foo//bar */ + /* -> foo/bar */ +#ifdef CYGWIN + if (p == buf) { /* //DRIVE/foo */ + p += 2; + q += 2; + continue; + } +#endif /* CYGWIN */ + *p = '\0'; + q++; + strcat(buf, q); + } + else { + p++; + q++; + } + } + return buf; +} + +/* string search using the simplest algorithm */ +char * +strcasestr(char *s1, char *s2) +{ + int len1, len2; + len1 = strlen(s1); + len2 = strlen(s2); + while (*s1 && len1 >= len2) { + if (strncasecmp(s1, s2, len2) == 0) + return s1; + s1++; + len1--; + } + return 0; +} + +static int +strcasematch(char *s1, char *s2) +{ + int x; + while (*s1) { + if (*s2 == '\0') + return 1; + x = tolower(*s1) - tolower(*s2); + if (x != 0) + break; + s1++; + s2++; + } + return (*s2 == '\0'); +} + +/* search multiple strings */ +int +strcasemstr(char *str, char *srch[], char **ret_ptr) +{ + int i; + while (*str) { + for (i = 0; srch[i]; i++) { + if (strcasematch(str, srch[i])) { + if (ret_ptr) + *ret_ptr = str; + return i; + } + } + str++; + } + return -1; +} + +char * +cleanup_str(char *str) +{ + Str tmp = NULL; + char *s = str, *c; + + while (*s) { + if (*s == '&') { + if (tmp == NULL) { + tmp = Strnew(); + Strcat_charp_n(tmp, str, s - str); + } + c = getescapecmd(&s); + Strcat_charp(tmp, c); + } + else { + if (tmp) + Strcat_char(tmp, *s); + s++; + } + } + + if (tmp) + return tmp->ptr; + else + return str; +} + +char * +remove_space(char *str) +{ + Str s = Strnew(); + while (*str) { + if (!IS_SPACE(*str)) + Strcat_char(s, *str); + str++; + } + return s->ptr; +} + +char * +htmlquote_char(char c) +{ + switch (c) { + case '&': + return "&"; + case '<': + return "<"; + case '>': + return ">"; + case '"': + return """; + } + return NULL; +} + +char * +htmlquote_str(char *str) +{ + Str tmp = NULL; + char *p, *q; + for (p = str; *p; p++) { + q = htmlquote_char(*p); + if (q) { + if (tmp == NULL) { + tmp = Strnew(); + Strcat_charp_n(tmp, str, p - str); + } + Strcat_charp(tmp, q); + } + else { + if (tmp) + Strcat_char(tmp, *p); + } + } + if (tmp) + return tmp->ptr; + else + return str; +} + +Str +form_quote(Str x) +{ + Str r = Strnew(); + int i; + char c; + for (i = 0; i < x->length; i++) { + c = x->ptr[i]; + if (c == ' ') + Strcat_char(r, '+'); + else if (IS_ALNUM(c) || is_safe(c)) { + Strcat_char(r, c); + } + else { + Strcat_charp(r, "%"); + Strcat(r, Sprintf("%02X", (c & 0xff))); + } + } + return r; +} + +/* rfc1808 safe */ +static int +is_safe(int c) +{ + switch (c) { + /* safe */ + case '$': + case '-': + case '_': + case '.': + return 1; + default: + return 0; + } +} + +Str +form_unquote(Str x) +{ + Str r = Strnew(); + int i, j; + char c; + Str num; + + for (i = 0; i < x->length; i++) { + c = x->ptr[i]; + if (c == '+') + Strcat_char(r, ' '); + else if (c == '%') { + num = Strnew_charp("0"); + if (IS_ALNUM(x->ptr[i + 1])) { + Strcat_char(num, x->ptr[i + 1]); + i++; + if (IS_ALNUM(x->ptr[i + 1])) { + Strcat_char(num, x->ptr[i + 1]); + i++; + } + } + sscanf(num->ptr, "%x", &j); + Strcat_char(r, (char) j); + } + else + Strcat_char(r, c); + } + return r; +} + +char * +expandPath(char *name) +{ + Str userName = NULL; + char *p; + struct passwd *passent, *getpwnam(const char *); + Str extpath = Strnew(); + + if (name == NULL) + return NULL; + p = name; + if (*p == '~') { + p++; + if (IS_ALPHA(*p)) { + userName = Strnew(); + while (IS_ALNUM(*p) || *p == '_' || *p == '-') + Strcat_char(userName, *(p++)); + passent = getpwnam(userName->ptr); + if (passent == NULL) { + p = name; + goto rest; + } + Strcat_charp(extpath, passent->pw_dir); + } + else { + Strcat_charp(extpath, getenv("HOME")); + } + if (Strcmp_charp(extpath, "/") == 0 && *p == '/') + p++; + } + rest: + Strcat_charp(extpath, p); + return extpath->ptr; +} + +Str +escape_shellchar(Str s) +{ + Str x = Strnew(); + int i; + for (i = 0; i < s->length; i++) { + switch (s->ptr[i]) { + case ';': + case '&': + case '|': + case '$': + case '!': + case '(': + case ')': + case '{': + case '}': + case '*': + case '?': + Strcat_char(x, '\\'); + /* continue to the next */ + default: + Strcat_char(x, s->ptr[i]); + } + } + return x; +} + +int +non_null(char *s) +{ + if (s == NULL) + return FALSE; + while (*s) { + if (!IS_SPACE(*s)) + return TRUE; + s++; + } + return FALSE; +} + +void +cleanup_line(Str s, int mode) +{ + if (s->length >= 2 && + s->ptr[s->length - 2] == '\r' && + s->ptr[s->length - 1] == '\n') { + Strshrink(s, 2); + Strcat_char(s, '\n'); + } + else if (Strlastchar(s) == '\r') + s->ptr[s->length - 1] = '\n'; + else if (Strlastchar(s) != '\n') + Strcat_char(s, '\n'); + if (mode != PAGER_MODE) { + int i; + for (i = 0; i < s->length; i++) { + if (s->ptr[i] == '\0') + s->ptr[i] = ' '; + } + } +} + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ @@ -0,0 +1,43 @@ +#ifndef INDEP_H +#define INDEP_H +#include "gc.h" +#include "Str.h" + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +#define PAGER_MODE 0 +#define HTML_MODE 1 +#define HEADER_MODE 2 + +extern char *conv_latin1(int ch); +extern int getescapechar(char **s); +extern char *getescapecmd(char **s); +extern char *allocStr(const char *s, int len); +extern int strCmp(const void *s1, const void *s2); +extern void copydicname(char *s, char *fn); +extern char *currentdir(void); +extern char *cleanupName(char *name); +extern char *strcasestr(char *s1, char *s2); +extern int strcasemstr(char *str, char *srch[], char **ret_ptr); +extern char *cleanup_str(char *s); +extern char *remove_space(char *str); +extern char *htmlquote_char(char c); +extern char *htmlquote_str(char *str); +extern Str form_quote(Str x); +extern Str form_unquote(Str x); +extern char *expandPath(char *name); +extern int non_null(char *s); +extern void cleanup_line(Str s, int mode); + +#define New(type) ((type*)GC_MALLOC(sizeof(type))) +#define NewAtom(type) ((type*)GC_MALLOC_ATOMIC(sizeof(type))) +#define New_N(type,n) ((type*)GC_MALLOC((n)*sizeof(type))) +#define NewAtom_N(type,n) ((type*)GC_MALLOC_ATOMIC((n)*sizeof(type))) +#define New_Reuse(type,ptr,n) ((type*)GC_REALLOC((ptr),(n)*sizeof(type))) + +#endif /* INDEP_H */ diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..562f91a --- /dev/null +++ b/install.sh @@ -0,0 +1,33 @@ +#! /bin/sh + +while : +do + case $1 in + -m) + mode=$2 + shift; shift + ;; + -*) + shift + ;; + *) + break + esac +done + +if [ $# -lt 2 ]; then + echo "usage: $0 [-m mode] file1 file2" + exit 1 +fi + +file=$1 +dest=$2 + +cp $file $dest +if [ -n "$mode" ]; then + if [ -d $dest ]; then + chmod $mode $dest/$file + else + chmod $mode $dest + fi +fi diff --git a/islang.c b/islang.c new file mode 100644 index 0000000..af124c7 --- /dev/null +++ b/islang.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2000, NBG01720@nifty.ne.jp + * + * To compile this program: + * gcc -Zomf -Zcrtdll -O2 -Wall -s islang.c + */ +#define INCL_DOSNLS +#include <os2.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +int main(int argc,char**argv){ + if(argc<=1) + return 1; + + if(isdigit((int)*argv[1])){ + ULONG CpList[8],CpSize; + APIRET rc=DosQueryCp(sizeof(CpList),CpList,&CpSize); + if(rc) + return rc; + while(--argc>0) + if(*CpList==atoi(argv[argc])) + return 0; + }else{ + char*lang=getenv("LANG"); + if(!lang||!*lang){ + lang=getenv("LANGUAGE"); + if(!lang||!*lang) + return 1; + } + if(!strnicmp(lang,argv[1],2)) + return 0; + } + return 1; +} diff --git a/istream.c b/istream.c new file mode 100644 index 0000000..27b8d47 --- /dev/null +++ b/istream.c @@ -0,0 +1,481 @@ +/* $Id: istream.c,v 1.1 2001/11/08 05:15:57 a-ito Exp $ */ +#include "fm.h" +#include "istream.h" +#include <signal.h> + +#define uchar unsigned char + +#define STREAM_BUF_SIZE 8192 +#define SSL_BUF_SIZE 1536 + +#define MUST_BE_UPDATED(bs) ((bs)->stream.cur==(bs)->stream.next) + +#define POP_CHAR(bs) ((bs)->iseos?'\0':(bs)->stream.buf[(bs)->stream.cur++]) + +static void basic_close(int *handle); +static int basic_read(int *handle, char *buf, int len); + +static void file_close(struct file_handle *handle); +static int file_read(struct file_handle *handle, char *buf, int len); + +static int str_read(Str handle, char *buf, int len); + +#ifdef USE_SSL +static void ssl_close(struct ssl_handle *handle); +static int ssl_read(struct ssl_handle *handle, char *buf, int len); +#endif + +static int ens_read(struct ens_handle *handle, char *buf, int len); +static void ens_close(struct ens_handle *handle); + +static void +do_update(BaseStream base) +{ + int len; + base->stream.cur = base->stream.next = 0; + len = base->read(base->handle, base->stream.buf, base->stream.size); + if (len <= 0) + base->iseos = TRUE; + else + base->stream.next += len; +} + +static int +buffer_read(StreamBuffer sb, char *obuf, int count) +{ + int len = sb->next - sb->cur; + if (len > 0) { + if (len > count) + len = count; + bcopy((const void *)&sb->buf[sb->cur], obuf, len); + sb->cur += len; + } + return len; +} + +static void +init_buffer(BaseStream base, char *buf, int bufsize) +{ + StreamBuffer sb = &base->stream; + sb->size = bufsize; + sb->cur = 0; + if (buf) { + sb->buf = (uchar *)buf; + sb->next = bufsize; + } + else { + sb->buf = NewAtom_N(uchar, bufsize); + sb->next = 0; + } + base->iseos = FALSE; +} + +static void +init_base_stream(BaseStream base, int bufsize) +{ + init_buffer(base, NULL, bufsize); +} + +static void +init_str_stream(BaseStream base, Str s) +{ + init_buffer(base, s->ptr, s->length); +} + +InputStream +newInputStream(int des) +{ + InputStream stream; + if (des < 0) + return NULL; + stream = New(union input_stream); + init_base_stream(&stream->base, STREAM_BUF_SIZE); + stream->base.type = IST_BASIC; + stream->base.handle = New(int); + *(int *)stream->base.handle = des; + stream->base.read = (int (*)()) basic_read; + stream->base.close = (void (*)()) basic_close; + return stream; +} + +InputStream +newFileStream(FILE *f, void (*closep)()) +{ + InputStream stream; + if (f == NULL) + return NULL; + stream = New(union input_stream); + init_base_stream(&stream->base, STREAM_BUF_SIZE); + stream->file.type = IST_FILE; + stream->file.handle = New(struct file_handle); + stream->file.handle->f = f; + if (closep) + stream->file.handle->close = closep; + else + stream->file.handle->close = (void (*)()) fclose; + stream->file.read = (int (*)()) file_read; + stream->file.close = (void (*)()) file_close; + return stream; +} + +InputStream +newStrStream(Str s) +{ + InputStream stream; + if (s == NULL) + return NULL; + stream = New(union input_stream); + init_str_stream(&stream->base, s); + stream->str.type = IST_STR; + stream->str.handle = s; + stream->str.read = (int (*)()) str_read; + stream->str.close = NULL; + return stream; +} + +#ifdef USE_SSL +InputStream +newSSLStream(SSL *ssl, int sock) +{ + InputStream stream; + if (sock < 0) + return NULL; + stream = New(union input_stream); + init_base_stream(&stream->base, SSL_BUF_SIZE); + stream->ssl.type = IST_SSL; + stream->ssl.handle = New(struct ssl_handle); + stream->ssl.handle->ssl = ssl; + stream->ssl.handle->sock = sock; + stream->ssl.read = (int (*)()) ssl_read; + stream->ssl.close = (void (*)()) ssl_close; + return stream; +} +#endif + +InputStream +newEncodedStream(InputStream is, char encoding) +{ + InputStream stream; + if (is == NULL || (encoding != ENC_QUOTE && encoding != ENC_BASE64)) + return is; + stream = New(union input_stream); + init_base_stream(&stream->base, STREAM_BUF_SIZE); + stream->ens.type = IST_ENCODED; + stream->ens.handle = New(struct ens_handle); + stream->ens.handle->is = is; + stream->ens.handle->pos = 0; + stream->ens.handle->encoding = encoding; + stream->ens.handle->s = NULL; + stream->ens.read = (int (*)()) ens_read; + stream->ens.close = (void (*)()) ens_close; + return stream; +} + +void +ISclose(InputStream stream) +{ + MySignalHandler(*prevtrap) (); + if (stream == NULL || stream->base.close == NULL) + return; + prevtrap = signal(SIGINT, SIG_IGN); + stream->base.close(stream->base.handle); + signal(SIGINT, prevtrap); +} + +int +ISgetc(InputStream stream) +{ + BaseStream base; + if (stream == NULL) + return '\0'; + base = &stream->base; + if (!base->iseos && MUST_BE_UPDATED(base)) + do_update(base); + return POP_CHAR(base); +} + +int +ISundogetc(InputStream stream) +{ + StreamBuffer sb; + if (stream == NULL) + return -1; + sb = &stream->base.stream; + if (sb->cur > 0) { + sb->cur--; + return 0; + } + return -1; +} + +#define MARGIN_STR_SIZE 10 +Str +StrISgets(InputStream stream) +{ + BaseStream base; + StreamBuffer sb; + Str s = NULL; + uchar *p; + int len; + + if (stream == NULL) + return '\0'; + base = &stream->base; + sb = &base->stream; + + while (!base->iseos) { + if (MUST_BE_UPDATED(base)) { + do_update(base); + } + else { + if (p = memchr(&sb->buf[sb->cur], '\n', sb->next - sb->cur)) { + len = p - &sb->buf[sb->cur] + 1; + if (s == NULL) + s = Strnew_size(len); + Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len); + sb->cur += len; + return s; + } + else { + if (s == NULL) + s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE); + Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur); + sb->cur = sb->next; + } + } + } + + if (s == NULL) + return Strnew(); + return s; +} + +Str +StrmyISgets(InputStream stream) +{ + BaseStream base; + StreamBuffer sb; + Str s = NULL; + int i, len; + + if (stream == NULL) + return '\0'; + base = &stream->base; + sb = &base->stream; + + while (!base->iseos) { + if (MUST_BE_UPDATED(base)) { + do_update(base); + } + else { + if (s && Strlastchar(s) == '\r') { + if (sb->buf[sb->cur] == '\n') + Strcat_char(s, (char)sb->buf[sb->cur++]); + return s; + } + for (i = sb->cur; + i < sb->next && sb->buf[i] != '\n' && sb->buf[i] != '\r'; + i++); + if (i < sb->next) { + len = i - sb->cur + 1; + if (s == NULL) + s = Strnew_size(len + MARGIN_STR_SIZE); + Strcat_charp_n(s, (char *)&sb->buf[sb->cur], len); + sb->cur = i + 1; + if (sb->buf[i] == '\n') + return s; + } + else { + if (s == NULL) + s = Strnew_size(sb->next - sb->cur + MARGIN_STR_SIZE); + Strcat_charp_n(s, (char *)&sb->buf[sb->cur], sb->next - sb->cur); + sb->cur = sb->next; + } + } + } + + if (s == NULL) + return Strnew(); + return s; +} + +int +ISread(InputStream stream, Str buf, int count) +{ + int rest, len; + BaseStream base; + + if (stream == NULL || (base = &stream->base)->iseos) + return 0; + + len = buffer_read(&base->stream, buf->ptr, count); + rest = count - len; + if (MUST_BE_UPDATED(base)) { + len = base->read(base->handle, &buf->ptr[len], rest); + if (len <= 0) { + base->iseos = TRUE; + len = 0; + } + rest -= len; + } + Strtruncate(buf, count - rest); + if (buf->length > 0) + return 1; + return 0; +} + +int +ISfileno(InputStream stream) +{ + if (stream == NULL) + return -1; + switch (IStype(stream)) { + case IST_BASIC: + return *(int *)stream->base.handle; + case IST_FILE: + return fileno(stream->file.handle->f); + case IST_ENCODED: + return ISfileno(stream->ens.handle->is); + default: + return -1; + } +} + +int +ISeos(InputStream stream) +{ + BaseStream base = &stream->base; + if (!base->iseos && MUST_BE_UPDATED(base)) + do_update(base); + return base->iseos; +} + +#ifdef USE_SSL +Str +ssl_get_certificate(InputStream stream) +{ + BIO *bp; + X509 *x; + char *p; + int len; + Str s; + if (stream == NULL) + return NULL; + if (IStype(stream) != IST_SSL) + return NULL; + if (stream->ssl.handle == NULL) + return NULL; + x = SSL_get_peer_certificate(stream->ssl.handle->ssl); + if (x == NULL) + return NULL; + bp = BIO_new(BIO_s_mem()); + X509_print(bp, x); + len = (int)BIO_ctrl(bp, BIO_CTRL_INFO,0,(char *)&p); + s = Strnew_charp_n(p, len); + BIO_free_all(bp); + return s; +} +#endif + +/* Raw level input stream functions */ + +static void +basic_close(int *handle) +{ + close(*(int *)handle); +} + +static int +basic_read(int *handle, char *buf, int len) +{ + return read(*(int *)handle, buf, len); +} + +static void +file_close(struct file_handle *handle) +{ + handle->close(handle->f); +} + +static int +file_read(struct file_handle *handle, char *buf, int len) +{ + return fread(buf, 1, len, handle->f); +} + +static int +str_read(Str handle, char *buf, int len) +{ + return 0; +} + +#ifdef USE_SSL +static void +ssl_close(struct ssl_handle *handle) +{ + close(handle->sock); + if (handle->ssl) + SSL_free(handle->ssl); +} + +static int +ssl_read(struct ssl_handle *handle, char *buf, int len) +{ + int status; + if (handle->ssl) { +#ifdef USE_SSL_VERIFY + for (;;) { + status = SSL_read(handle->ssl, buf, len); + if (status > 0) + break; + switch (SSL_get_error(handle->ssl, status)) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: /* reads can trigger write errors; see SSL_get_error(3) */ + continue; + default: + break; + } + break; + } +#else /* if !defined(USE_SSL_VERIFY) */ + status = SSL_read(handle->ssl, buf, len); +#endif /* !defined(USE_SSL_VERIFY) */ + } + else + status = read(handle->sock, buf, len); + return status; +} +#endif /* USE_SSL */ + +static void +ens_close(struct ens_handle *handle) +{ + ISclose(handle->is); +} + +static int +ens_read(struct ens_handle *handle, char *buf, int len) +{ + if (handle->s == NULL || handle->pos == handle->s->length) { + char *p; + handle->s = StrmyISgets(handle->is); + if (handle->s->length == 0) + return 0; + cleanup_line(handle->s, PAGER_MODE); + if (handle->encoding == ENC_BASE64) + Strchop(handle->s); + p = handle->s->ptr; + if (handle->encoding == ENC_QUOTE) + handle->s = decodeQP(&p); + else if (handle->encoding == ENC_BASE64) + handle->s = decodeB(&p); + handle->pos = 0; + } + + if (len > handle->s->length - handle->pos) + len = handle->s->length - handle->pos; + + bcopy(&handle->s->ptr[handle->pos], buf, len); + handle->pos += len; + return len; +} diff --git a/istream.h b/istream.h new file mode 100644 index 0000000..8c670d4 --- /dev/null +++ b/istream.h @@ -0,0 +1,153 @@ +/* $Id: istream.h,v 1.1 2001/11/08 05:15:57 a-ito Exp $ */ +#ifndef IO_STREAM_H +#define IO_STREAM_H + +#include <stdio.h> +#ifdef USE_SSL +#include <bio.h> +#include <x509.h> +#include <ssl.h> +#endif +#include "Str.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +struct stream_buffer { + unsigned char *buf; + int size, cur, next; +}; + +typedef struct stream_buffer *StreamBuffer; + +struct file_handle { + FILE *f; + void (*close) (); +}; + +#ifdef USE_SSL +struct ssl_handle { + SSL *ssl; + int sock; +}; +#endif + +union input_stream; + +struct ens_handle { + union input_stream *is; + Str s; + int pos; + char encoding; +}; + + +struct base_stream { + struct stream_buffer stream; + void *handle; + char type; + char iseos; + int (*read) (); + void (*close) (); +}; + +struct file_stream { + struct stream_buffer stream; + struct file_handle *handle; + char type; + char iseos; + int (*read) (); + void (*close) (); +}; + +struct str_stream { + struct stream_buffer stream; + Str handle; + char type; + char iseos; + int (*read) (); + void (*close) (); +}; + +#ifdef USE_SSL +struct ssl_stream { + struct stream_buffer stream; + struct ssl_handle *handle; + char type; + char iseos; + int (*read) (); + void (*close) (); +}; +#endif /* USE_SSL */ + +struct encoded_stream { + struct stream_buffer stream; + struct ens_handle *handle; + char type; + char iseos; + int (*read) (); + void (*close) (); +}; + +union input_stream { + struct base_stream base; + struct file_stream file; + struct str_stream str; +#ifdef USE_SSL + struct ssl_stream ssl; +#endif /* USE_SSL */ + struct encoded_stream ens; +}; + +typedef struct base_stream *BaseStream; +typedef struct file_stream *FileStream; +typedef struct str_stream *StrStream; +#ifdef USE_SSL +typedef struct ssl_stream *SSLStream; +#endif /* USE_SSL */ +typedef struct encoded_stream *EncodedStrStream; + +typedef union input_stream *InputStream; + +extern InputStream newInputStream(int des); +extern InputStream newFileStream(FILE * f, void (*closep)()); +extern InputStream newStrStream(Str s); +#ifdef USE_SSL +extern InputStream newSSLStream(SSL * ssl, int sock); +#endif +extern InputStream newEncodedStream(InputStream is, char encoding); +extern void ISclose(InputStream stream); +extern int ISgetc(InputStream stream); +extern int ISundogetc(InputStream stream); +extern Str StrISgets(InputStream stream); +extern Str StrmyISgets(InputStream stream); +extern int ISread(InputStream stream, Str buf, int count); +extern int ISfileno(InputStream stream); +extern int ISeos(InputStream stream); +#ifdef USE_SSL +extern Str ssl_get_certificate(InputStream stream); +#endif + +#define IST_BASIC 0 +#define IST_FILE 1 +#define IST_STR 2 +#define IST_SSL 3 +#define IST_ENCODED 4 + +#define IStype(stream) ((stream)->base.type) +#define is_eos(stream) ISeos(stream) +#define iseos(stream) ((stream)->base.iseos) +#define file_of(stream) ((stream)->file.handle->f) +#define set_close(stream,closep) ((IStype(stream)==IST_FILE)?((stream)->file.handle->close=(closep)):0) +#define str_of(stream) ((stream)->str.handle) +#ifdef USE_SSL +#define ssl_socket_of(stream) ((stream)->ssl.handle->sock) +#define ssl_of(stream) ((stream)->ssl.handle->ssl) +#endif + +#ifdef __CYGWIN__ +#define openIS(path) newInputStream(open((path),O_RDONLY|O_BINARY)) +#else +#define openIS(path) newInputStream(open((path),O_RDONLY)) +#endif /* __CYGWIN__ */ +#endif diff --git a/keybind.c b/keybind.c new file mode 100644 index 0000000..81ebaa3 --- /dev/null +++ b/keybind.c @@ -0,0 +1,198 @@ +/* $Id: keybind.c,v 1.1 2001/11/08 05:15:02 a-ito Exp $ */ +#include "funcname2.h" + +char GlobalKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ +#ifdef __EMX__ + pcmap, linbeg, movL, nulcmd, nulcmd, linend, movR, curlno, +#else + _mark, linbeg, movL, nulcmd, nulcmd, linend, movR, curlno, +#endif +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + ldHist, nextA, followA,cooLst, rdrwSc, followA,movD, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + movU, nulcmd, srchbak,srchfor,nulcmd, prevA, pgFore, wrapToggle, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, susp, escmap, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + pgFore, execsh, reMark, pipesh, linend, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, nulcmd, col1L, nulcmd, col1R, srchfor, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, chkURL, nulcmd, shiftl, pginfo, shiftr, srchbak, +/* @ A B C D E F G */ + readsh, nulcmd, backBf, nulcmd, nulcmd, editBf, rFrame, goLineL, +/* H I J K L M N O */ + ldhelp, followI,lup1, ldown1, nulcmd, extbrz, srchprv, nulcmd, +/* P Q R S T U V W */ + nulcmd, quitfm, reload, svBuf, nulcmd, goURL, ldfile, movLW, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, ctrCsrH,topA, nulcmd, lastA, linbeg, nulcmd, +/* ` a b c d e f g */ + nulcmd, svA, pgBack, curURL, nulcmd, nulcmd, nulcmd, goLineF, +/* h i j k l m n o */ + movL, peekIMG,movD, movU, movR, nulcmd, srchnxt, ldOpt, +/* p q r s t u v w */ + nulcmd, qquitfm,nulcmd, selBuf, nulcmd, peekURL,vwSrc, movRW, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, ctrCsrV,nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + nulcmd, prevA, svA, nulcmd, nulcmd, svA, nulcmd, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, chkNMID,nulcmd, goLineF,nulcmd, goLineL,nulcmd, +/* @ A B C D E F G */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* H I J K L M N O */ + nulcmd, svI, nulcmd, nulcmd, nulcmd, linkbrz,nulcmd, escbmap, +/* P Q R S T U V W */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, dictwordat, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, nulcmd, escbmap,nulcmd, nulcmd, nulcmd, nulcmd, +/* ` a b c d e f g */ + nulcmd, adBmark,ldBmark,nulcmd, nulcmd, editScr,nulcmd, goLine, +/* h i j k l m n o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nextMk, nulcmd, +/* p q r s t u v w */ + prevMk, nulcmd, nulcmd, svSrc, nulcmd, nulcmd, pgBack, dictword, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscBKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* @ A B C D E F G */ + nulcmd, movU, movD, movR, movL, nulcmd, goLineL, pgFore, +/* H I J K L M N O */ + goLineF, pgBack, nulcmd, nulcmd, nulcmd, mouse, nulcmd, nulcmd, +/* P Q R S T U V W */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ` a b c d e f g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* h i j k l m n o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* p q r s t u v w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscDKeymap[128] = +{ +/* 0 1 INS 3 4 PgUp, PgDn 7 */ + nulcmd, goLineF,mainMn, nulcmd, goLineL,pgBack, pgFore, nulcmd, +/* 8 9 10 F1 F2 F3 F4 F5 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 16 F6 F7 F8 F9 F10 22 23 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 24 25 26 27 HELP 29 30 31 */ + nulcmd, nulcmd, nulcmd, nulcmd, mainMn, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +#ifdef __EMX__ +char PcKeymap[256]={ +// Null + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 0 +// S-Tab + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, prevA, // 8 +// A-q A-w A-E A-r A-t A-y A-u A-i + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 16 +// A-o A-p A-[ A-] A-a A-s + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 24 +// A-d A-f A-g A-h A-j A-k A-l A-; + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 32 +// A-' A-' A-\ A-x A-c A-v + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 40 +// A-b A-n A-m A-, A-. A-/ A-+ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 48 +// F1 F2 F3 F4 F5 + nulcmd, nulcmd, nulcmd, ldhelp, nulcmd, qquitfm,nulcmd, nulcmd, // 56 +// F6 F7 F8 F9 F10 Home + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, goLineF, // 64 +// Up PgUp A-/ Left 5 Right C-* End + movU, pgBack, nulcmd, movL, nulcmd, movR, nulcmd, goLineL, // 72 +// Down PgDn Ins Del S-F1 S-F2 S-F3 S-F4 + movD, pgFore, mainMn, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 80 +// S-F5 S-F6 S-F7 S-F8 S-F9 S-F10 C-F1 C-F2 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 88 +// C-F3 C-F4 C-F5 C-F6 C-F7 C-F8 C-F9 C-F10 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 96 +// A-F1 A-F2 A-F3 A-F4 A-F5 A-F6 A-F7 A-F8 + nulcmd, nulcmd, nulcmd, qquitfm,nulcmd, nulcmd, nulcmd, nulcmd, // 104 +// A-F9 A-F10 PrtSc C-Left C-Right C-End C-PgDn C-Home + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 112 +// A-1 A-2 A-3 A-4 A-5 A-6 A-7/8 A-9 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 120 +// A-0 A - A-= C-PgUp F11 F12 S-F11 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 128 +// S-F12 C-F11 C-F12 A-F11 A-F12 C-Up C-/ C-5 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 136 +// S-* C-Down C-Ins C-Del C-Tab C - C-+ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 144 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 152 +// A - A-Tab A-Enter + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 160 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 168 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 176 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 184 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 192 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 200 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 208 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 216 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 224 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 232 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, // 240 + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd // 248 +}; +#endif diff --git a/keybind_lynx.c b/keybind_lynx.c new file mode 100644 index 0000000..0ca8b02 --- /dev/null +++ b/keybind_lynx.c @@ -0,0 +1,149 @@ + +/* + * Lynx-like key binding. + * + * modified from original keybind.c by Keisuke Hashimoto + * <hasimoto@shimada.nuee.nagoya-u.ac.jp> + * http://www.shimada.nuee.nagoya-u.ac.jp/~hasimoto/ + * + * further modification by Akinori Ito + * + * Date: Tue, 23 Feb 1999 13:14:44 +0900 + */ + +#include "funcname2.h" + +char GlobalKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + _mark, goLineF,backBf, nulcmd, nulcmd, goLineL,followA,nulcmd, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + ldHist, nextA, followA, cooLst, rdrwSc, followA, nextA, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + prevA, nulcmd, reload, srchfor, nulcmd, nulcmd, pgFore, rdrwSc, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, susp, escmap, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + pgFore, execsh, reMark, pipesh, linend, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, pgFore, nulcmd, pgBack, nulcmd, srchfor, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, chkURL, nulcmd, shiftl, pginfo, shiftr, ldhelp, +/* @ A B C D E F G */ + readsh, nulcmd, backBf, nulcmd, nulcmd, editBf, rFrame, goLine, +/* H I J K L M N O */ + ldhelp, followI,lup1, ldown1, nulcmd, extbrz, nextMk, nulcmd, +/* P Q R S T U V W */ + prevMk, quitfm, reload, svBuf, nulcmd, goURL, ldfile, nulcmd, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, ctrCsrH, nulcmd, vwSrc, nulcmd, linbeg, nulcmd, +/* ` a b c d e f g */ + nulcmd, adBmark,pgBack, curURL, svA, nulcmd, nulcmd, goURL, +/* h i j k l m n o */ + movL, peekIMG,movD, movU, movR, nulcmd, srchnxt,ldOpt, +/* p q r s t u v w */ + svBuf, qquitfm,nulcmd, selBuf, nulcmd, peekURL,ldBmark,wrapToggle, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, ctrCsrV,nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + nulcmd, prevA, svA, nulcmd, nulcmd, svA, nulcmd, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, chkNMID, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* @ A B C D E F G */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* H I J K L M N O */ + nulcmd, svI, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, escbmap, +/* P Q R S T U V W */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, nulcmd, escbmap,nulcmd, nulcmd, nulcmd, nulcmd, +/* ` a b c d e f g */ + nulcmd, adBmark,ldBmark,nulcmd, nulcmd, editScr,nulcmd, nulcmd, +/* h i j k l m n o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* p q r s t u v w */ + nulcmd, nulcmd, nulcmd, svSrc, nulcmd, nulcmd, pgBack, nulcmd, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscBKeymap[128] = +{ +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* SPC ! " # $ % & ' */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ( ) * + , - . / */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 0 1 2 3 4 5 6 7 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 8 9 : ; < = > ? */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* @ A B C D E F G */ + nulcmd, prevA, nextA, followA, backBf, nulcmd, nulcmd, nulcmd, +/* H I J K L M N O */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, mouse, nulcmd, nulcmd, +/* P Q R S T U V W */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* X Y Z [ \ ] ^ _ */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* ` a b c d e f g */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* h i j k l m n o */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* p q r s t u v w */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* x y z { | } ~ DEL */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; + +char EscDKeymap[128] = +{ +/* 0 1 INS 3 4 PgUp, PgDn 7 */ + nulcmd, goLineF,mainMn, nulcmd, goLineL,pgBack, pgFore, nulcmd, +/* 8 9 10 F1 F2 F3 F4 F5 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 16 F6 F7 F8 F9 F10 22 23 */ + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +/* 24 25 26 27 HELP 29 30 31 */ + nulcmd, nulcmd, nulcmd, nulcmd, mainMn, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, + nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, nulcmd, +}; diff --git a/linein.c b/linein.c new file mode 100644 index 0000000..a6ca795 --- /dev/null +++ b/linein.c @@ -0,0 +1,1009 @@ +/* $Id: linein.c,v 1.1 2001/11/08 05:15:04 a-ito Exp $ */ +#include "fm.h" +#include "local.h" +#include "myctype.h" + +#ifdef MOUSE +#ifdef USE_GPM +#include <gpm.h> +#endif +#if defined(USE_GPM) || defined(USE_SYSMOUSE) +extern int do_getch(); +#define getch() do_getch() +#endif /* USE_GPM */ +#endif /* MOUSE */ + +#ifdef __EMX__ +#include <sys/kbdscan.h> +#endif + +#define STR_LEN 1024 +#define CLEN (COLS - 2) + +static Str strBuf; +static Lineprop strProp[STR_LEN]; + +static Str CompleteBuf; +static Str CFileName; +static Str CBeforeBuf; +static Str CAfterBuf; +static Str CDirBuf; +static char **CFileBuf = NULL; +static int NCFileBuf; +static int NCFileOffset; + +static void insertself(char c), + _mvR(void), _mvL(void), _mvRw(void), _mvLw(void), delC(void), insC(void), + _mvB(void), _mvE(void), _enter(void), _quo(void), _bs(void), _bsw(void), + killn(void), killb(void), _inbrk(void), _esc(void), + _prev(void), _next(void), _compl(void), _rcompl(void), _tcompl(void), + _dcompl(void), _rdcompl(void); +#ifdef _EMX_ +static int getcntrl(); +#endif + +static int terminated(unsigned char c); +#define iself ((void(*)())insertself) + +static void next_compl(int next); +static void next_dcompl(int next); +static Str doComplete(Str ifn, int *status, int next); + +void (*InputKeymap[32]) () = { +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ + _compl, _mvB, _mvL, _inbrk, delC, _mvE, _mvR, _inbrk, +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + _bs, iself, _enter, killn, iself, _enter, _next, iself, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + _prev, _quo, _bsw, iself, _mvLw, killb, _quo, iself, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + _tcompl,_mvRw, iself, _esc, iself, iself, iself, iself, +}; + +static int setStrType(Str str, Lineprop * prop); +static void addPasswd(char *p, Lineprop *pr, int len, int pos, int limit); +static void addStr(char *p, Lineprop *pr, int len, int pos, int limit); + +static int CPos, CLen, offset; +static int i_cont, i_broken, i_quote; +static int cm_mode, cm_next, cm_clear, cm_disp_next, cm_disp_clear; +static int need_redraw, is_passwd; +static int move_word; + +static Hist *CurrentHist; +static Str strCurrentBuf; +static int use_hist; +#ifdef JP_CHARSET +static int in_kanji; +static void ins_kanji(Str tmp); +#endif + +char * +inputLineHist(char *prompt, char *def_str, int flag, Hist * hist) +{ + int opos, x, y, lpos, rpos, epos; + unsigned char c; + char *p; + Lineprop mode; +#ifdef JP_CHARSET + Str tmp = Strnew(); +#endif /* JP_CHARSET */ + + mode = PC_ASCII; +#ifdef JP_CHARSET + in_kanji = FALSE; +#endif + is_passwd = FALSE; + move_word = FALSE; + + CurrentHist = hist; + if (hist != NULL) { + use_hist = TRUE; + strCurrentBuf = NULL; + } else { + use_hist = FALSE; + } + if (flag & IN_URL) { + cm_mode = CPL_ALWAYS | CPL_URL; + move_word = TRUE; + } else if (flag & IN_FILENAME) { + cm_mode = CPL_ALWAYS; + move_word = TRUE; + } else if (flag & IN_PASSWORD) { + cm_mode = CPL_NEVER; + is_passwd = TRUE; + } else if (flag & IN_COMMAND) + cm_mode = CPL_ON; + else + cm_mode = CPL_OFF; + opos = strlen(prompt); + epos = CLEN - opos; + if (epos < 0) + epos = 0; + lpos = epos / 3; + rpos = epos * 2 / 3; + offset = 0; + + if (def_str) { + strBuf = Strnew_charp(def_str); + CLen = CPos = setStrType(strBuf, strProp); + } else { + strBuf = Strnew(); + CLen = CPos = 0; + } + + i_cont = TRUE; + i_broken = FALSE; + i_quote = FALSE; + cm_next = FALSE; + cm_disp_next = -1; + need_redraw = FALSE; + do { + x = calcPosition(strBuf->ptr, strProp, CLen, CPos, 0, CP_FORCE); + if (x - rpos > offset) { + y = calcPosition(strBuf->ptr, strProp, CLen, CLen, 0, CP_AUTO); + if (y - epos > x - rpos) + offset = x - rpos; + else if (y - epos > 0) + offset = y - epos; + } else if (x - lpos < offset) { + if (x - lpos > 0) + offset = x - lpos; + else + offset = 0; + } + move(LASTLINE, 0); + addstr(prompt); + if (is_passwd) + addPasswd(strBuf->ptr, strProp, CLen, offset, COLS - opos); + else + addStr(strBuf->ptr, strProp, CLen, offset, COLS - opos); + clrtoeolx(); + move(LASTLINE, opos + x - offset); + refresh(); + + next_char: + c = getch(); +#ifdef __EMX__ + if (c == 0) { + if (!(c = getcntrl())) + goto next_char; + } +#endif + cm_clear = TRUE; + cm_disp_clear = TRUE; +#ifdef JP_CHARSET + if (mode == PC_KANJI1) { + mode = PC_KANJI2; + if (CLen >= STR_LEN) + goto next_char; + Strcat_char(tmp, (c | (DisplayCode == CODE_SJIS ? 0 : 0x80))); + tmp = conv_str(tmp, + (DisplayCode == CODE_SJIS ? CODE_SJIS : CODE_EUC), InnerCode); + ins_kanji(tmp); + } + else +#endif + if (!i_quote && + (((cm_mode & CPL_ALWAYS) && (c == CTRL_I || c == ' ')) || + ((cm_mode & CPL_ON) && (c == CTRL_I)))) { +#ifdef EMACS_LIKE_LINEEDIT + if (cm_next) { + _dcompl(); + need_redraw = TRUE; + } + else { + _compl(); + cm_disp_next = -1; + } +#else + _compl(); + cm_disp_next = -1; + } + else if (!i_quote && CLen == CPos && + (cm_mode & CPL_ALWAYS || cm_mode & CPL_ON) && c == CTRL_D) { + _dcompl(); + need_redraw = TRUE; +#endif + } + else if (!i_quote && c == DEL_CODE) { + _bs(); + cm_next = FALSE; + cm_disp_next = -1; + } + else if (!i_quote && c < 0x20) { /* Control code */ + (*InputKeymap[(int) c]) (c); + if (cm_clear) + cm_next = FALSE; + if (cm_disp_clear) + cm_disp_next = -1; + } +#ifdef JP_CHARSET + else if (DisplayCode == CODE_SJIS && 0xa0 <= c && c <= 0xdf) { + i_quote = FALSE; + cm_next = FALSE; + if (CLen >= STR_LEN) + goto next_char; + Strclear(tmp); + Strcat_char(tmp, c); + tmp = conv_str(tmp, DisplayCode, InnerCode); + ins_kanji(tmp); + } + else if ((c & 0x80) || in_kanji) { /* Kanji 1 */ + i_quote = FALSE; + cm_next = FALSE; + cm_disp_next = -1; + if (CLen >= STR_LEN - 1) + goto next_char; + Strclear(tmp); + Strcat_char(tmp, (c | 0x80)); + mode = PC_KANJI1; + goto next_char; + } +#endif /* JP_CHARSET */ + else { + i_quote = FALSE; + cm_next = FALSE; + cm_disp_next = -1; + if (CLen >= STR_LEN) + goto next_char; + insC(); + strBuf->ptr[CPos] = c; + if (!is_passwd && IS_CNTRL(c)) + strProp[CPos] = PC_CTRL; + else + strProp[CPos] = PC_ASCII; + CPos++; + mode = PC_ASCII; + } + } while (i_cont); + + if (need_redraw) + displayBuffer(Currentbuf, B_FORCE_REDRAW); + + if (i_broken) + return NULL; + move(LASTLINE, 0); + refresh(); + p = strBuf->ptr; + if (flag & (IN_FILENAME | IN_COMMAND)) { + SKIP_BLANKS(p); + } + if (use_hist && !(flag & IN_URL) && *p != '\0') { + char *q = lastHist(hist); + if (! q || strcmp(q, p)) + pushHist(hist, p); + } + if (flag & IN_FILENAME) + return expandName(p); + else + return allocStr(p, 0); +} + +#ifdef __EMX__ +static int +getcntrl(void) +{ + switch (getch()) { + case K_DEL: + return CTRL_D; + case K_LEFT: + retrun CTRL_B; + case K_RIGHT: + return CTRL_F; + case K_UP: + return CTRL_P; + case K_DOWN: + return CTRL_N; + case K_HOME: + case K_CTRL_LEFT: + return CTRL_A; + case K_END: + case K_CTRL_RIGHT: + return CTRL_E; + case K_CTRL_HOME: + return CTRL_U; + case K_CTRL_END: + return CTRL_K; + } + return 0; +} +#endif + +static void +addPasswd(char *p, Lineprop *pr, int len, int offset, int limit) +{ + int rcol = 0, ncol; + + ncol = calcPosition(p, pr, len, len, 0, CP_AUTO); + if (ncol> offset + limit) + ncol = offset + limit; + if (offset) { + addChar('{', 0); + rcol = offset + 1; + } + for (; rcol < ncol; rcol++) + addChar('*', 0); +} + +static void +addStr(char *p, Lineprop *pr, int len, int offset, int limit) +{ + int i = 0, rcol = 0, ncol, delta = 1; + + if (offset) { + for (i = 0; i < len; i++) { + if (calcPosition(p, pr, len, i, 0, CP_AUTO) > offset) + break; + } + if (i >= len) + return; +#ifdef JP_CHARSET + while (pr[i] == PC_KANJI2) + i++; +#endif + addChar('{', 0); + rcol = offset + 1; + ncol = calcPosition(p, pr, len, i, 0, CP_AUTO); + for (; rcol < ncol; rcol++) + addChar(' ', 0); + } + for (; i < len; i += delta) { +#ifdef JP_CHARSET + if (CharType(pr[i]) == PC_KANJI1) + delta = 2; + else + delta = 1; +#endif + ncol = calcPosition(p, pr, len, i + delta, 0, CP_AUTO); + if (ncol - offset > limit) + break; + if (p[i] == '\t') { + for (; rcol < ncol; rcol++) + addChar(' ', 0); + continue; +#ifdef JP_CHARSET + } else if (delta == 2) { + addChar(p[i], pr[i]); + addChar(p[i+1], pr[i+1]); +#endif + } else + addChar(p[i], pr[i]); + rcol = ncol; + } +} + +#ifdef JP_CHARSET +static void +ins_kanji(Str tmp) +{ + if (tmp->length != 2) + return; + insC(); + strBuf->ptr[CPos] = tmp->ptr[0]; + strProp[CPos] = PC_KANJI1; + CPos++; + insC(); + strBuf->ptr[CPos] = tmp->ptr[1]; + strProp[CPos] = PC_KANJI2; + CPos++; +} +#endif + +static void +_esc(void) +{ + char c, c2; + + switch (c = getch()) { + case '[': + case 'O': + switch (c = getch()) { + case 'A': + _prev(); + break; + case 'B': + _next(); + break; + case 'C': + _mvR(); + break; + case 'D': + _mvL(); + break; + } + break; + case CTRL_I: + case ' ': +#ifdef EMACS_LIKE_LINEEDIT + _rdcompl(); + cm_clear = FALSE; +#else + _rcompl(); + break; + case CTRL_D: + _rdcompl(); +#endif + need_redraw = TRUE; + break; +#ifdef EMACS_LIKE_LINEEDIT + case 'f': + _mvRw(); + break; + case 'b': + _mvLw(); + break; + case CTRL_H: + _bsw(); + break; +#endif +#ifdef JP_CHARSET + case '$': + /* ISO-2022-jp characters */ + c2 = getch(); + in_kanji = TRUE; + break; + case '(': + /* ISO-2022-jp characters */ + c2 = getch(); + in_kanji = FALSE; + break; +#endif + } +} + +static void +insC(void) +{ + int i; + + Strinsert_char(strBuf, CPos, ' '); + CLen = strBuf->length; + for (i = CLen; i > CPos; i--) { + strProp[i] = strProp[i - 1]; + } +} + +static void +delC(void) +{ + int i = CPos; + int delta = 1; + + if (CLen == CPos) + return; +#ifdef JP_CHARSET + if (strProp[i] == PC_KANJI1) + delta = 2; +#endif /* JP_CHARSET */ + for (i = CPos; i < CLen; i++) { + strProp[i] = strProp[i + delta]; + } + Strdelete(strBuf, CPos, delta); + CLen -= delta; +} + +static void +_mvL(void) +{ + if (CPos > 0) + CPos--; +#ifdef JP_CHARSET + if (strProp[CPos] == PC_KANJI2) + CPos--; +#endif /* JP_CHARSET */ +} + +static void +_mvLw(void) +{ + int first = 1; + while(CPos > 0 + && ( first || !terminated(strBuf->ptr[CPos-1]))) + { + CPos--; + first = 0; +#ifdef JP_CHARSET + if (strProp[CPos] == PC_KANJI2) + CPos--; +#endif /* JP_CHARSET */ + if (!move_word) + break; + } +} + +static void +_mvRw(void) +{ + int first = 1; + while(CPos < CLen + && ( first || !terminated(strBuf->ptr[CPos-1]))) + { + CPos++; + first = 0; +#ifdef JP_CHARSET + if (strProp[CPos] == PC_KANJI2) + CPos++; +#endif /* JP_CHARSET */ + if(!move_word) + break; + } +} + +static void +_mvR(void) +{ + if (CPos < CLen) + CPos++; +#ifdef JP_CHARSET + if (strProp[CPos] == PC_KANJI2) + CPos++; +#endif /* JP_CHARSET */ +} + +static void +_bs(void) +{ + if (CPos > 0) { + _mvL(); + delC(); + } +} + +static void +_bsw(void) +{ + int t = 0; + while(CPos > 0 && !t) { + _mvL(); + t = (move_word && terminated(strBuf->ptr[CPos-1])); + delC(); + } +} + +static void +_enter(void) +{ + i_cont = FALSE; +} + +static void +insertself(char c) +{ + if (CLen >= STR_LEN) + return; + insC(); + strBuf->ptr[CPos] = c; + strProp[CPos] = (is_passwd) ? PC_ASCII : PC_CTRL; + CPos++; +} + +static void +_quo(void) +{ + i_quote = TRUE; +} + +static void +_mvB(void) +{ + CPos = 0; +} + +static void +_mvE(void) +{ + CPos = CLen; +} + +static void +killn(void) +{ + CLen = CPos; + Strtruncate(strBuf, CLen); +} + +static void +killb(void) +{ + while (CPos > 0) + _bs(); +} + +static void +_inbrk(void) +{ + i_cont = FALSE; + i_broken = TRUE; +} + +static void +_compl(void) +{ + next_compl(1); +} + +static void +_rcompl(void) +{ + next_compl(-1); +} + +static void +_tcompl(void) +{ + if (cm_mode & CPL_OFF) + cm_mode = CPL_ON; + else if (cm_mode & CPL_ON) + cm_mode = CPL_OFF; +} + +static void +next_compl(int next) +{ + int status; + int b, a; + Str buf; + Str s; + + if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF) + return; + cm_clear = FALSE; + if (!cm_next) { + for (b = CPos - 1; b >= 0; b--) { + if (strBuf->ptr[b] == ' ' || strBuf->ptr[b] == CTRL_I) + break; + } + b++; + a = CPos; + CBeforeBuf = Strsubstr(strBuf, 0, b); + buf = Strsubstr(strBuf, b, a - b); + CAfterBuf = Strsubstr(strBuf, a, strBuf->length - a); + s = doComplete(buf, &status, next); + } + else { + s = doComplete(strBuf, &status, next); + } + if (next == 0) + return; + + if (status != CPL_OK && status != CPL_MENU) + bell(); + if (status == CPL_FAIL) + return; + + strBuf = Strnew_m_charp(CBeforeBuf->ptr, s->ptr, CAfterBuf->ptr, NULL); + CLen = setStrType(strBuf, strProp); + CPos = CBeforeBuf->length + s->length; + if (CPos > CLen) + CPos = CLen; +} + +static void +_dcompl(void) +{ + next_dcompl(1); +} + +static void +_rdcompl(void) +{ + next_dcompl(-1); +} + +static void +next_dcompl(int next) +{ + static int col, row, len; + static Str d; + int i, j, n, y; + Str f; + char *p; + struct stat st; + int comment, nline; + + if (cm_mode == CPL_NEVER || cm_mode & CPL_OFF) + return; + cm_disp_clear = FALSE; + displayBuffer(Currentbuf, B_FORCE_REDRAW); + + if (LASTLINE >= 3) { + comment = TRUE; + nline = LASTLINE - 2; + } else if (LASTLINE) { + comment = FALSE; + nline = LASTLINE; + } else { + return; + } + if (cm_disp_next >= 0) { + if (next == 1) { + cm_disp_next += col * nline; + if (cm_disp_next >= NCFileBuf) + cm_disp_next = 0; + } + else if (next == -1) { + cm_disp_next -= col * nline; + if (cm_disp_next < 0) + cm_disp_next = 0; + } + row = (NCFileBuf - cm_disp_next + col - 1) / col; + goto disp_next; + } + + cm_next = FALSE; + next_compl(0); + if (NCFileBuf == 0) + return; + cm_disp_next = 0; + + d = Strdup(CDirBuf); + if (d->length > 0 && Strlastchar(d) != '/') + Strcat_char(d, '/'); + if (cm_mode & CPL_URL && d->ptr[0] == 'f') { + p = d->ptr; + if (strncmp(p, "file://localhost/", 17) == 0) + p = &p[16]; + else if (strncmp(p, "file:///", 8) == 0) + p = &p[7]; + else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/') + p = &p[5]; + d = Strnew_charp(p); + } + + len = 0; + for (i = 0; i < NCFileBuf; i++) { + n = strlen(CFileBuf[i]) + 3; + if (len < n) + len = n; + } + col = COLS / len; + if (col == 0) + col = 1; + row = (NCFileBuf + col - 1) / col; + +disp_next: + if (comment) { + if (row > nline) { + row = nline; + y = 0; + } else + y = nline - row + 1; + } else { + if (row >= nline) { + row = nline; + y = 0; + } else + y = nline - row - 1; + } + if (y) { + move(y - 1, 0); + clrtoeolx(); + } + if (comment) { + move(y, 0); + clrtoeolx(); + bold(); + addstr("----- Completion list -----"); + boldend(); + y++; + } + for (i = 0; i < row; i++) { + for (j = 0; j < col; j++) { + n = cm_disp_next + j * row + i; + if (n >= NCFileBuf) + break; + move(y, j * len); + clrtoeolx(); + f = Strdup(d); + Strcat_charp(f, CFileBuf[n]); + addstr(CFileBuf[n]); + if (stat(expandName(f->ptr), &st) != -1 && S_ISDIR(st.st_mode)) + addstr("/"); + } + y++; + } + if (comment && y == LASTLINE - 1) { + move(y, 0); + clrtoeolx(); + bold(); +#ifdef EMACS_LIKE_LINEEDIT + addstr("----- Press TAB to continue -----"); +#else + addstr("----- Press CTRL-D to continue -----"); +#endif + boldend(); + } +} + +static Str +doComplete(Str ifn, int *status, int next) +{ + int fl, i; + char *fn, *p; + DIR *d; + Directory *dir; + struct stat st; + + if (!cm_next) { + NCFileBuf = 0; + CompleteBuf = Strdup(ifn); + while (Strlastchar(CompleteBuf) != '/' && + CompleteBuf->length > 0) + Strshrink(CompleteBuf, 1); + CDirBuf = Strdup(CompleteBuf); + if (cm_mode & CPL_URL) { + if (strncmp(CompleteBuf->ptr, "file://localhost/", 17) == 0) + Strdelete(CompleteBuf, 0, 16); + else if (strncmp(CompleteBuf->ptr, "file:///", 8) == 0) + Strdelete(CompleteBuf, 0, 7); + else if (strncmp(CompleteBuf->ptr, "file:/", 6) == 0 && + CompleteBuf->ptr[6] != '/') + Strdelete(CompleteBuf, 0, 5); + else { + CompleteBuf = Strdup(ifn); + *status = CPL_FAIL; + return CompleteBuf; + } + } + if (CompleteBuf->length == 0) { + Strcat_char(CompleteBuf, '.'); + } + if (Strlastchar(CompleteBuf) == '/' && CompleteBuf->length > 1) { + Strshrink(CompleteBuf, 1); + } + if ((d = opendir(expandName(CompleteBuf->ptr))) == NULL) { + CompleteBuf = Strdup(ifn); + *status = CPL_FAIL; + return CompleteBuf; + } + fn = lastFileName(ifn->ptr); + fl = strlen(fn); + CFileName = Strnew(); + for (;;) { + dir = readdir(d); + if (dir == NULL) + break; + if (fl == 0 && (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."))) + continue; + if (!strncmp(dir->d_name, fn, fl)) { /* match */ + NCFileBuf++; + CFileBuf = New_Reuse(char *, CFileBuf, NCFileBuf); + CFileBuf[NCFileBuf - 1] = New_N(char, strlen(dir->d_name) + 1); + strcpy(CFileBuf[NCFileBuf - 1], dir->d_name); + if (NCFileBuf == 1) { + CFileName = Strnew_charp(dir->d_name); + } + else { + for (i = 0; CFileName->ptr[i] == dir->d_name[i]; i++); + Strtruncate(CFileName, i); + } + } + } + closedir(d); + if (NCFileBuf == 0) { + CompleteBuf = Strdup(ifn); + *status = CPL_FAIL; + return CompleteBuf; + } + qsort(CFileBuf, NCFileBuf, sizeof(CFileBuf[0]), strCmp); + NCFileOffset = 0; + if (NCFileBuf >= 2) { + cm_next = TRUE; + *status = CPL_AMBIG; + } + else { + *status = CPL_OK; + } + } + else { + CFileName = Strnew_charp(CFileBuf[NCFileOffset]); + NCFileOffset = (NCFileOffset + next + NCFileBuf) % NCFileBuf; + *status = CPL_MENU; + } + CompleteBuf = Strdup(CDirBuf); + if (CompleteBuf->length == 0) + Strcat(CompleteBuf, CFileName); + else if (Strlastchar(CompleteBuf) == '/') + Strcat(CompleteBuf, CFileName); + else { + Strcat_char(CompleteBuf, '/'); + Strcat(CompleteBuf, CFileName); + } + if (*status != CPL_AMBIG) { + p = CompleteBuf->ptr; + if (cm_mode & CPL_URL) { + if (strncmp(p, "file://localhost/", 17) == 0) + p = &p[16]; + else if (strncmp(p, "file:///", 8) == 0) + p = &p[7]; + else if (strncmp(p, "file:/", 6) == 0 && p[6] != '/') + p = &p[5]; + } + if (stat(expandName(p), &st) != -1 && S_ISDIR(st.st_mode)) + Strcat_char(CompleteBuf, '/'); + } + return CompleteBuf; +} + +static void +_prev(void) +{ + Hist *hist = CurrentHist; + char *p; + + if (! use_hist) + return; + if (strCurrentBuf) { + p = prevHist(hist); + if (p == NULL) + return; + } else { + p = lastHist(hist); + if (p == NULL) + return; + strCurrentBuf = strBuf; + } + strBuf = Strnew_charp(p); + CLen = CPos = setStrType(strBuf, strProp); + offset = 0; +} + +static void +_next(void) +{ + Hist *hist = CurrentHist; + char *p; + + if (! use_hist) + return; + if (strCurrentBuf == NULL) + return; + p = nextHist(hist); + if (p) { + strBuf = Strnew_charp(p); + } else { + strBuf = strCurrentBuf; + strCurrentBuf = NULL; + } + CLen = CPos = setStrType(strBuf, strProp); + offset = 0; +} + +static int +setStrType(Str str, Lineprop * prop) +{ + Lineprop ctype; + int i = 0, delta; + char *s = str->ptr; + + for (; *s != '\0' && i < STR_LEN; s += delta, i += delta) { + ctype = get_mctype(s); + if (is_passwd && ctype & PC_CTRL) + ctype = PC_ASCII; + delta = get_mclen(ctype); +#ifdef JP_CHARSET + if (ctype == PC_KANJI) { + prop[i] = PC_KANJI1; + prop[i+1] = PC_KANJI2; + } + else +#endif + prop[i] = ctype; + } + return i; +} + +static int +terminated(unsigned char c){ + int termchar[] = {'/', '&', '?', -1}; + int *tp; + + for(tp = termchar; *tp > 0; tp++){ + if(c == *tp){ + return 1; + } + } + + return 0; +} @@ -0,0 +1,388 @@ +#include "fm.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#ifdef READLINK +#include <unistd.h> +#endif /* READLINK */ +#include "local.h" + +#define CGIFN_NORMAL 0 +#define CGIFN_DROOT 1 +#define CGIFN_CGIBIN 2 + +/* setup cookie for local CGI */ +void +setLocalCookie() +{ + Str buf; + char hostname[256]; + gethostname(hostname,256); + buf = Sprintf("%d.%ld@%s",getpid(),lrand48(),hostname); + Local_cookie = buf->ptr; +} + +Buffer * +dirBuffer(char *dname) +{ + Str tmp; + DIR *d; + Directory *dir; + struct stat st; + char **flist; + char *p, *qdir; + Str fbuf = Strnew(); +#ifdef READLINK + struct stat lst; + char lbuf[1024]; +#endif /* READLINK */ + int i, l, nrow, n = 0, maxlen = 0; + int nfile, nfile_max = 100; + Str dirname; + + d = opendir(dname); + if (d == NULL) + return NULL; + dirname = Strnew_charp(dname); + qdir = htmlquote_str(dirname->ptr); + tmp = Sprintf("<title>Directory list of %s</title><h1>Directory list of %s</h1>\n", qdir, qdir); + flist = New_N(char *, nfile_max); + nfile = 0; + while ((dir = readdir(d)) != NULL) { + flist[nfile++] = allocStr(dir->d_name, 0); + if (nfile == nfile_max) { + nfile_max *= 2; + flist = New_Reuse(char *, flist, nfile_max); + } + if (multicolList) { + l = strlen(dir->d_name); + if (l > maxlen) + maxlen = l; + n++; + } + } + + if (multicolList) { + l = COLS / (maxlen + 2); + if (!l) + l = 1; + nrow = (n + l - 1) / l; + n = 1; + Strcat_charp(tmp, "<TABLE CELLPADDING=0><TR VALIGN=TOP>\n"); + } + qsort((void *) flist, nfile, sizeof(char *), strCmp); + for (i = 0; i < nfile; i++) { + p = flist[i]; + if (strcmp(p, ".") == 0) + continue; + Strcopy(fbuf, dirname); + if (Strlastchar(fbuf) != '/') + Strcat_char(fbuf, '/'); + Strcat_charp(fbuf, p); +#ifdef READLINK + if (lstat(fbuf->ptr, &lst) < 0) + continue; +#endif /* READLINK */ + if (stat(fbuf->ptr, &st) < 0) + continue; + if (multicolList) { + if (n == 1) + Strcat_charp(tmp, "<TD><NOBR>"); + } + else { + if (S_ISDIR(st.st_mode)) + Strcat_charp(tmp, "[DIR] "); +#ifdef READLINK + else if (S_ISLNK(lst.st_mode)) + Strcat_charp(tmp, "[LINK] "); +#endif /* READLINE */ + else + Strcat_charp(tmp, "[FILE] "); + } + Strcat_m_charp(tmp, "<A HREF=\"", htmlquote_str(fbuf->ptr), NULL); + if (S_ISDIR(st.st_mode)) + Strcat_char(tmp, '/'); + Strcat_m_charp(tmp, "\">", htmlquote_str(p), NULL); + if (S_ISDIR(st.st_mode)) + Strcat_char(tmp, '/'); + Strcat_charp(tmp, "</a>"); + if (multicolList) { + if (n++ == nrow) { + Strcat_charp(tmp, "</NOBR></TD>\n"); + n = 1; + } + else { + Strcat_charp(tmp, "<BR>\n"); + } + } + else { +#ifdef READLINK + if (S_ISLNK(lst.st_mode)) { + if ((l = readlink(fbuf->ptr, lbuf, sizeof(lbuf))) > 0) { + lbuf[l] = '\0'; + Strcat_m_charp(tmp, " -> ", htmlquote_str(lbuf), NULL); + if (S_ISDIR(st.st_mode)) + Strcat_char(tmp, '/'); + } + } +#endif /* READLINK */ + Strcat_charp(tmp, "<br>\n"); + } + } + if (multicolList) { + Strcat_charp(tmp, "</TR></TABLE>\n"); + } + + return loadHTMLString(tmp); +} + +#ifdef __EMX__ +char * +get_os2_dft(const char *name, char *dft) +{ + char *value = getenv(name); + return value ? value : dft; +} + +#define lib_dir get_os2_dft("W3M_LIB_DIR",LIB_DIR) +#else /* not __EMX__ */ +#define lib_dir LIB_DIR +#endif /* not __EMX__ */ + +static int +check_local_cgi(char *file, int status) +{ + struct stat st; + +#ifdef __EMX__ + if (status != CGIFN_CGIBIN) { + char tmp[_MAX_PATH]; + + _abspath(tmp, lib_dir, _MAX_PATH); /* Translate '\\' to '/' + * + */ + if (strnicmp(file, tmp, strlen(tmp))) /* and ignore case */ + return -1; + } +#else /* not __EMX__ */ + if (status != CGIFN_CGIBIN && + strncmp(file, lib_dir, strlen(lib_dir)) != 0) { + /* + * a local-CGI script should be located on either + * /cgi-bin/ directory or LIB_DIR (typically /usr/local/lib/w3m). + */ + return -1; + } +#endif /* not __EMX__ */ + if (stat(file, &st) < 0) + return -1; + if ((st.st_uid == geteuid() && (st.st_mode & S_IXUSR)) || + (st.st_gid == getegid() && (st.st_mode & S_IXGRP)) || + (st.st_mode & S_IXOTH)) { /* executable */ + return 0; + } + return -1; +} + +void +set_environ(char *var, char *value) +{ +#ifdef HAVE_SETENV + setenv(var, value, 1); +#else /* not HAVE_SETENV */ +#ifdef HAVE_PUTENV + Str tmp = Strnew_m_charp(var, "=", value, NULL); + putenv(tmp->ptr); +#else /* not HAVE_PUTENV */ + extern char **environ; + char **ne; + char *p; + int i, l, el; + char **e, **newenv; + + /* I have no setenv() nor putenv() */ + /* This part is taken from terms.c of skkfep */ + l = strlen(var); + for (e = environ, i = 0; *e != NULL; e++, i++) { + if (strncmp(e, var, l) == 0 && (*e)[l] == '=') { + el = strlen(*e) - l - 1; + if (el >= strlen(value)) { + strcpy(*e + l + 1, value); + return 0; + } + else { + for (; *e != NULL; e++, i++) { + *e = *(e + 1); + } + i--; + break; + } + } + } + newenv = (char **) GC_malloc((i + 2) * sizeof(char *)); + if (newenv == NULL) + return; + for (e = environ, ne = newenv; *e != NULL; *(ne++) = *(e++)); + *(ne++) = p; + *ne = NULL; + environ = newenv; +#endif /* not HAVE_PUTENV */ +#endif /* not HAVE_SETENV */ +} + +static void +set_cgi_environ(char *name, char *fn, char *req_uri) +{ + set_environ("SERVER_SOFTWARE", version); + set_environ("SERVER_PROTOCOL", "HTTP/1.0"); + set_environ("SERVER_NAME", "localhost"); + set_environ("SERVER_PORT", "80"); /* dummy */ + set_environ("REMOTE_HOST", "localhost"); + set_environ("REMOTE_ADDR", "127.0.0.1"); + set_environ("GATEWAY_INTERFACE", "CGI/1.1"); + + set_environ("SCRIPT_NAME", name); + set_environ("SCRIPT_FILENAME", fn); + set_environ("REQUEST_URI", req_uri); + set_environ("LOCAL_COOKIE",Local_cookie); +} + +static Str +checkPath(char *fn, char *path) +{ + Str tmp; + struct stat st; + while (*path) { + tmp = Strnew(); + while (*path && *path != ':') + Strcat_char(tmp, *path++); + if (*path == ':') + path++; + if (Strlastchar(tmp) != '/') + Strcat_char(tmp, '/'); + Strcat_charp(tmp, fn); + if (stat(tmp->ptr, &st) == 0) + return tmp; + } + return NULL; +} + +static char * +cgi_filename(char *fn, int *status) +{ + Str tmp; + struct stat st; + if (cgi_bin != NULL && strncmp(fn, "/cgi-bin/", 9) == 0) { + *status = CGIFN_CGIBIN; + tmp = checkPath(fn + 9, cgi_bin); + if (tmp == NULL) + return fn; + return tmp->ptr; + } + if (strncmp(fn, "/$LIB/", 6) == 0) { + *status = CGIFN_NORMAL; + tmp = Strnew_charp(lib_dir); + fn += 5; + if (Strlastchar(tmp) == '/') + fn++; + Strcat_charp(tmp, fn); + return tmp->ptr; + } + if (*fn == '/' && document_root != NULL && stat(fn, &st) < 0) { + *status = CGIFN_DROOT; + tmp = Strnew_charp(document_root); + if (Strlastchar(tmp) != '/') + Strcat_char(tmp, '/'); + Strcat_charp(tmp, fn); + return tmp->ptr; + } + *status = CGIFN_NORMAL; + return fn; +} + +FILE * +localcgi_post(char *file, FormList * request, char *referer) +{ + FILE *f; + Str tmp1, tmp2; + int status; + + tmp1 = Strnew_charp(file); + file = cgi_filename(file, &status); + if (check_local_cgi(file, status) < 0) + return NULL; + set_cgi_environ(tmp1->ptr, file, tmp1->ptr); + set_environ("REQUEST_METHOD", "POST"); + set_environ("CONTENT_LENGTH", Sprintf("%d", request->length)->ptr); + if (referer && referer != NO_REFERER) + set_environ("HTTP_REFERER",referer); + if (request->enctype == FORM_ENCTYPE_MULTIPART) { + set_environ("CONTENT_TYPE", + Sprintf("multipart/form-data; boundary=%s", request->boundary)->ptr); + } + else { + set_environ("CONTENT_TYPE", "application/x-www-form-urlencoded"); + } + tmp1 = tmpfname(TMPF_DFL, NULL); + f = fopen(tmp1->ptr, "w"); + if (f == NULL) + return NULL; + pushText(fileToDelete, tmp1->ptr); + if (request->enctype == FORM_ENCTYPE_MULTIPART) { + FILE *fd; + int c; + fd = fopen(request->body, "r"); + if (fd != NULL) { + while ((c = fgetc(fd)) != EOF) + fputc(c, f); + fclose(fd); + } + } + else { + fputs(request->body, f); + } + fclose(f); + tmp2 = Sprintf("%s < %s", file, tmp1->ptr); +#ifdef __EMX__ + f = popen(tmp2->ptr, "r"); +#else + tmp1 = Strnew_charp(CurrentDir); + chdir(mydirname(file)); + f = popen(tmp2->ptr, "r"); + chdir(tmp1->ptr); +#endif + return f; +} + +FILE * +localcgi_get(char *file, char *request, char *referer) +{ + FILE *f; + Str tmp1, tmp2; + int status; + + tmp1 = Strnew_charp(file); + file = cgi_filename(file, &status); + if (check_local_cgi(file, status) < 0) + return NULL; + if (!strcmp(request, "")) { + set_cgi_environ(tmp1->ptr, file, tmp1->ptr); + } else { + set_cgi_environ(tmp1->ptr, file, + Strnew_m_charp(tmp1->ptr, "?", request, NULL)->ptr); + } + if (referer && referer != NO_REFERER) + set_environ("HTTP_REFERER",referer); + set_environ("REQUEST_METHOD", "GET"); + set_environ("QUERY_STRING", request); + tmp2 = Sprintf("%s", file); +#ifdef __EMX__ + f = popen(tmp2->ptr, "r"); +#else + tmp1 = Strnew_charp(CurrentDir); + chdir(mydirname(file)); + f = popen(tmp2->ptr, "r"); + chdir(tmp1->ptr); +#endif + return f; +} @@ -0,0 +1,44 @@ +/* + * w3m local.h + */ + +#ifndef LOCAL_H +#define LOCAL_H + +#include <sys/types.h> +#ifdef DIRENT +#include <dirent.h> +typedef struct dirent Directory; +#else /* not DIRENT */ +#include <sys/dir.h> +typedef struct direct Directory; +#endif /* not DIRENT */ +#include <sys/stat.h> + +#ifndef S_IFMT +#define S_IFMT 0170000 +#endif /* not S_IFMT */ +#ifndef S_IFREG +#define S_IFREG 0100000 +#endif /* not S_IFREG */ + +#define NOT_REGULAR(m) (((m) & S_IFMT) != S_IFREG) +#define IS_DIRECTORY(m) (((m) & S_IFMT) == S_IFDIR) + +#ifndef S_ISDIR +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif /* not S_IFDIR */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* not S_ISDIR */ + +#ifdef READLINK +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif /* not S_IFLNK */ +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif /* not S_ISLNK */ +#endif /* not READLINK */ + +#endif /* not LOCAL_H */ diff --git a/mailcap.c b/mailcap.c new file mode 100644 index 0000000..6c6e014 --- /dev/null +++ b/mailcap.c @@ -0,0 +1,324 @@ +/* $Id: mailcap.c,v 1.1 2001/11/08 05:15:04 a-ito Exp $ */ +#include "fm.h" +#include "myctype.h" +#include <stdio.h> +#include <errno.h> +#include "parsetag.h" +#include "local.h" + +static struct mailcap DefaultMailcap[] = +{ + {"image/*", "xv %s", 0, NULL, NULL, NULL}, /* */ + {"audio/basic", "showaudio %s", 0, NULL, NULL, NULL}, + {NULL, NULL, 0, NULL, NULL, NULL} +}; + +void +initMailcap() +{ + int i; + TextListItem *tl; + + if (non_null(mailcap_files)) + mailcap_list = make_domain_list(mailcap_files); + else + mailcap_list = NULL; + if (mailcap_list == NULL) + return; + UserMailcap = New_N(struct mailcap *, mailcap_list->nitem); + for (i = 0, tl = mailcap_list->first; tl; i++, tl = tl->next) + UserMailcap[i] = loadMailcap(tl->ptr); +} + +int +mailcapMatch(struct mailcap *mcap, char *type) +{ + char *cap = mcap->type, *p; + int level; + for (p = cap; *p != '/'; p++) { + if (tolower(*p) != tolower(*type)) + return 0; + type++; + } + if (*type != '/') + return 0; + p++; + type++; + if (mcap->flags & MAILCAP_HTMLOUTPUT) + level = 1; + else + level = 0; + if (*p == '*') + return 10 + level; + while (*p) { + if (tolower(*p) != tolower(*type)) + return 0; + p++; + type++; + } + if (*type != '\0') + return 0; + return 20 + level; +} + +struct mailcap * +searchMailcap(struct mailcap *table, char *type) +{ + int level = 0; + struct mailcap *mcap = NULL; + int i; + + if (table == NULL) + return NULL; + for (; table->type; table++) { + i = mailcapMatch(table, type); + if (i > level) { + if (table->test) { + Str command = unquote_mailcap(table->test, type, NULL, NULL); + if (system(command->ptr) != 0) + continue; + } + level = i; + mcap = table; + } + } + return mcap; +} + +static int +matchattr(char *p, char *attr, int len, Str * value) +{ + int quoted; + char *q = NULL; + + if (strncasecmp(p, attr, len) == 0) { + p += len; + SKIP_BLANKS(p); + if (value) { + *value = Strnew(); + if (*p == '=') { + p++; + SKIP_BLANKS(p); + quoted = 0; + while (*p && (quoted || *p != ';')) { + if (quoted || !IS_SPACE(*p)) + q = p; + if (quoted) + quoted = 0; + else if (*p == '\\') + quoted = 1; + Strcat_char(*value, *p); + p++; + } + if (q) + Strshrink(*value, p - q - 1); + } + return 1; + } + else { + if (*p == '\0' || *p == ';') { + return 1; + } + } + } + return 0; +} + +int +extractMailcapEntry(char *mcap_entry, struct mailcap *mcap) +{ + int j, k; + char *p; + int quoted; + Str tmp; + + bzero(mcap, sizeof(struct mailcap)); + p = mcap_entry; + SKIP_BLANKS(p); + k = -1; + for (j = 0; p[j] && p[j] != ';'; j++) { + if (!IS_SPACE(p[j])) + k = j; + } + mcap->type = allocStr(p, (k >= 0)? k + 1 : j); + if (!p[j]) + return 0; + p += j + 1; + + SKIP_BLANKS(p); + k = -1; + quoted = 0; + for (j = 0; p[j] && (quoted || p[j] != ';'); j++) { + if (quoted || !IS_SPACE(p[j])) + k = j; + if (quoted) + quoted = 0; + else if (p[j] == '\\') + quoted = 1; + } + mcap->viewer = allocStr(p, (k >= 0)? k + 1 : j); + p += j; + + while (*p == ';') { + p++; + SKIP_BLANKS(p); + if (matchattr(p, "needsterminal", 13, NULL)) { + mcap->flags |= MAILCAP_NEEDSTERMINAL; + } + else if (matchattr(p, "copiousoutput", 13, NULL)) { + mcap->flags |= MAILCAP_COPIOUSOUTPUT; + } + else if (matchattr(p, "htmloutput", 10, NULL)) { + mcap->flags |= MAILCAP_HTMLOUTPUT; + } + else if (matchattr(p, "test", 4, &tmp)) { + mcap->test = allocStr(tmp->ptr, tmp->length); + } + else if (matchattr(p, "nametemplate", 12, &tmp)) { + mcap->nametemplate = allocStr(tmp->ptr, tmp->length); + } + else if (matchattr(p, "edit", 4, &tmp)) { + mcap->edit = allocStr(tmp->ptr, tmp->length); + } + quoted = 0; + while (*p && (quoted || *p != ';')) { + if (quoted) + quoted = 0; + else if (*p == '\\') + quoted = 1; + p++; + } + } + return 1; +} + +struct mailcap * +loadMailcap(char *filename) +{ + FILE *f; + int i, n; + Str tmp; + struct mailcap *mcap; + + f = fopen(expandName(filename), "r"); + if (f == NULL) + return NULL; + i = 0; + while (tmp = Strfgets(f), tmp->length > 0) { + if (tmp->ptr[0] != '#') + i++; + } + fseek(f, 0, 0); + n = i; + mcap = New_N(struct mailcap, n + 1); + i = 0; + while (tmp = Strfgets(f), tmp->length > 0) { + if (tmp->ptr[0] == '#') + continue; + redo: + while (IS_SPACE(Strlastchar(tmp))) + Strshrink(tmp, 1); + if (Strlastchar(tmp) == '\\') { + /* continuation */ + Strshrink(tmp, 1); + Strcat(tmp, Strfgets(f)); + goto redo; + } + if (extractMailcapEntry(tmp->ptr, &mcap[i])) + i++; + } + bzero(&mcap[i], sizeof(struct mailcap)); + fclose(f); + return mcap; +} + +struct mailcap * +searchExtViewer(char *type) +{ + struct mailcap *p; + int i; + + if (mailcap_list == NULL) + goto no_user_mailcap; + + for (i = 0; i < mailcap_list->nitem; i++) { + if ((p = searchMailcap(UserMailcap[i], type)) != NULL) + return p; + } + + no_user_mailcap: + return searchMailcap(DefaultMailcap, type); +} + +#define MC_NORMAL 0 +#define MC_PREC 1 +#define MC_PREC2 2 +#define MC_QUOTED 3 + +Str +unquote_mailcap(char *qstr, char *type, char *name, int *stat) +{ + Str str = Strnew(); + char *p; + int status = MC_NORMAL; + + if (stat) + *stat = 0; + + if (qstr == NULL) + return NULL; + + for (p = qstr; *p; p++) { + if (status == MC_QUOTED) { + Strcat_char(str, *p); + status = MC_NORMAL; + continue; + } + else if (*p == '\\') { + status = MC_QUOTED; + continue; + } + switch (status) { + case MC_NORMAL: + if (*p == '%') { + status = MC_PREC; + } + else + Strcat_char(str, *p); + break; + case MC_PREC: + if (IS_ALPHA(*p)) { + switch (*p) { + case 's': + if (name) { + Strcat_charp(str, name); + if (stat) + *stat |= MCSTAT_REPNAME; + } + break; + case 't': + if (type) { + Strcat_charp(str, type); + if (stat) + *stat |= MCSTAT_REPTYPE; + } + break; + } + status = MC_NORMAL; + } + else if (*p == '{') { + status = MC_PREC2; + } + else if (*p == '%') { + Strcat_char(str, *p); + } + break; + case MC_PREC2: + if (*p == '}') { + status = MC_NORMAL; + } + break; + } + } + return str; +} @@ -0,0 +1,3990 @@ +/* $Id: main.c,v 1.1 2001/11/08 05:15:13 a-ito Exp $ */ +#define MAINPROGRAM +#include "fm.h" +#include <signal.h> +#include <setjmp.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <fcntl.h> +#include "terms.h" +#include "myctype.h" +#ifdef MOUSE +#ifdef USE_GPM +#include <gpm.h> +#endif /* USE_GPM */ +#if defined(USE_GPM) || defined(USE_SYSMOUSE) +extern int do_getch(); +#define getch() do_getch() +#endif /* defined(USE_GPM) || * + * defined(USE_SYSMOUSE) */ +#endif + +#define DSTR_LEN 256 + +Hist *LoadHist; +Hist *SaveHist; +Hist *URLHist; +Hist *ShellHist; +Hist *TextHist; + +#define N_EVENT_QUEUE 10 +typedef struct { + int cmd; + void *user_data; +} Event; +static Event eventQueue[N_EVENT_QUEUE]; +static int n_event_queue; + +#ifdef USE_MARK +static char *MarkString = NULL; +#endif +static char *SearchString = NULL; +int (*searchRoutine) (Buffer *, char *); + +JMP_BUF IntReturn; + +static void cmd_loadfile(char *path); +static void cmd_loadURL(char *url, ParsedURL * current); +static void cmd_loadBuffer(Buffer * buf, int prop, int link); +static void keyPressEventProc(int c); +#ifdef USE_MARK +static void cmd_mark(Lineprop * p); +#endif /* USE_MARK */ +#ifdef SHOW_PARAMS +int show_params_p = 0; +void show_params(FILE * fp); +#endif + +static int display_ok = FALSE; +int w3m_dump = 0; +int w3m_dump_source = 0; +int w3m_dump_head = 0; +static void dump_source(Buffer *); +static void dump_head(Buffer *); +int prec_num = 0; +int prev_key = -1; +int on_target = 1; + +static void _goLine(char*); +#define PREC_NUM (prec_num ? prec_num : 1) +#define PREC_LIMIT 10000 +#if 0 +static int searchKeyNum(void); +#endif + +#include "gcmain.c" + +#define help() fusage(stdout, 0) +#define usage() fusage(stderr, 1) + +static void +fusage(FILE *f, int err) +{ + fprintf(f, "version %s\n", version); + fprintf(f, "usage: w3m [options] [URL or filename]\noptions:\n"); + fprintf(f, " -t tab set tab width\n"); + fprintf(f, " -r ignore backspace effect\n"); + fprintf(f, " -l line # of preserved line (default 10000)\n"); +#ifdef JP_CHARSET +#ifndef DEBIAN /* disabled by ukai: -s is used for squeeze multi lines */ + fprintf(f, " -e EUC-JP\n"); + fprintf(f, " -s Shift_JIS\n"); + fprintf(f, " -j JIS\n"); +#endif + fprintf(f, " -I e|s document code\n"); +#endif /* JP_CHARSET */ + fprintf(f, " -B load bookmark\n"); + fprintf(f, " -bookmark file specify bookmark file\n"); + fprintf(f, " -T type specify content-type\n"); + fprintf(f, " -m internet message mode\n"); + fprintf(f, " -v visual startup mode\n"); +#ifdef COLOR + fprintf(f, " -M monochrome display\n"); +#endif /* COLOR */ + fprintf(f, " -F automatically render frame\n"); + fprintf(f, " -dump dump formatted page into stdout\n"); + fprintf(f, " -cols width specify column width (used with -dump)\n"); + fprintf(f, " -ppc count specify the number of pixels per character (4.0...32.0)\n"); + fprintf(f, " -dump_source dump page source into stdout\n"); + fprintf(f, " -dump_head dump response of HEAD request into stdout\n"); + fprintf(f, " +<num> goto <num> line\n"); + fprintf(f, " -num show line number\n"); + fprintf(f, " -no-proxy don't use proxy\n"); +#ifdef MOUSE + fprintf(f, " -no-mouse don't use mouse\n"); +#endif /* MOUSE */ +#ifdef USE_COOKIE + fprintf(f, " -cookie use cookie (-no-cookie: don't use cookie)\n"); + fprintf(f, " -pauth user:pass proxy authentication\n"); +#endif /* USE_COOKIE */ +#ifndef KANJI_SYMBOLS + fprintf(f, " -no-graph don't use graphic character\n"); +#endif /* not KANJI_SYMBOLS */ +#ifdef DEBIAN /* replaced by ukai: pager requires -s */ + fprintf(f, " -s squeeze multiple blank lines\n"); +#else + fprintf(f, " -S squeeze multiple blank lines\n"); +#endif + fprintf(f, " -W toggle wrap search mode\n"); + fprintf(f, " -X don't use termcap init/deinit\n"); + fprintf(f, " -o opt=value assign value to config option\n"); + fprintf(f, " -config file specify config file\n"); + fprintf(f, " -debug DO NOT USE\n"); +#ifdef SHOW_PARAMS + if (show_params_p) + show_params(f); +#endif + exit(err); +} + +static int option_assigned = 0; +extern void parse_proxy(void); + +static GC_warn_proc orig_GC_warn_proc = NULL; +#define GC_WARN_KEEP_MAX (20) + +static void +wrap_GC_warn_proc(char *msg, GC_word arg) +{ + if (fmInitialized) { + static struct { + char *msg; + GC_word arg; + } msg_ring[GC_WARN_KEEP_MAX] = { + {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, + {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, + {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, + {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, {NULL, 0}, + }; + static int i = 0; + static int n = 0; + static int lock = 0; + int j; + + j = (i + n) % (sizeof(msg_ring) / sizeof(msg_ring[0])); + msg_ring[j].msg = msg; + msg_ring[j].arg = arg; + + if (n < sizeof(msg_ring) / sizeof(msg_ring[0])) + ++n; + else + ++i; + + if (!lock) { + lock = 1; + + for (; n > 0 ; --n, ++i) { + i %= sizeof(msg_ring) / sizeof(msg_ring[0]); + disp_message_nsec(Sprintf(msg_ring[i].msg, (unsigned long)msg_ring[i].arg)->ptr, FALSE, 1, TRUE, FALSE); + } + + lock = 0; + } + } + else if (orig_GC_warn_proc) + orig_GC_warn_proc(msg, arg); + else + fprintf(stderr, msg, (unsigned long)arg); +} + +int +MAIN(int argc, char **argv, char **envp) +{ + Buffer *newbuf = NULL; + char *p, c; + int i; + InputStream redin; + char *line_str = NULL; + char **load_argv; + FormList *request; + int load_argc = 0; + int load_bookmark = FALSE; + int visual_start = FALSE; + +#ifndef SYS_ERRLIST + prepare_sys_errlist(); +#endif /* not SYS_ERRLIST */ + + srand48(time(0)); + + NO_proxy_domains = newTextList(); + fileToDelete = newTextList(); + + load_argv = New_N(char *, argc - 1); + load_argc = 0; + + CurrentDir = currentdir(); + BookmarkFile = NULL; + rc_dir = expandName(RC_DIR); + i = strlen(rc_dir); + if (i > 1 && rc_dir[i - 1] == '/') + rc_dir[i - 1] = '\0'; + config_file = rcFile(CONFIG_FILE); + create_option_search_table(); + + /* argument search 1 */ + for (i = 1; i < argc; i++) { + if (*argv[i] == '-') { + if (!strcmp("-config", argv[i])) { + argv[i] = "-dummy"; + if (++i >= argc) + usage(); + config_file = argv[i]; + argv[i] = "-dummy"; + } + else if (!strcmp("-h", argv[i])) + help(); + } + } + + /* initializations */ + init_rc(config_file); + initKeymap(); +#ifdef MENU + initMenu(); + CurrentMenuData = NULL; +#endif /* MENU */ +#ifdef USE_COOKIE + initCookie(); +#endif /* USE_COOKIE */ + setLocalCookie(); /* setup cookie for local CGI */ + + LoadHist = newHist(); + SaveHist = newHist(); + ShellHist = newHist(); + TextHist = newHist(); + URLHist = newHist(); +#ifdef USE_HISTORY + loadHistory(URLHist); +#endif /* not USE_HISTORY */ + + if (!non_null(HTTP_proxy) && + ((p = getenv("HTTP_PROXY")) || + (p = getenv("http_proxy")) || + (p = getenv("HTTP_proxy")))) { + HTTP_proxy = p; + parseURL(p, &HTTP_proxy_parsed, NULL); + } +#ifdef USE_GOPHER + if (!non_null(GOPHER_proxy) && + ((p = getenv("GOPHER_PROXY")) || + (p = getenv("gopher_proxy")) || + (p = getenv("GOPHER_proxy")))) { + GOPHER_proxy = p; + parseURL(p, &GOPHER_proxy_parsed, NULL); + } +#endif /* USE_GOPHER */ + if (!non_null(FTP_proxy) && + ((p = getenv("FTP_PROXY")) || + (p = getenv("ftp_proxy")) || + (p = getenv("FTP_proxy")))) { + FTP_proxy = p; + parseURL(p, &FTP_proxy_parsed, NULL); + } + if (!non_null(NO_proxy) && + ((p = getenv("NO_PROXY")) || + (p = getenv("no_proxy")) || + (p = getenv("NO_proxy")))) { + NO_proxy = p; + set_no_proxy(p); + } + + if (Editor == NULL && (p = getenv("EDITOR")) != NULL) + Editor = p; + if (Mailer == NULL && (p = getenv("MAILER")) != NULL) + Mailer = p; + + /* argument search 2 */ + i = 1; + while (i < argc) { + if (*argv[i] == '-') { + if (!strcmp("-t", argv[i])) { + if (++i >= argc) + usage(); + if (atoi(argv[i]) != 0) + Tabstop = atoi(argv[i]); + } + else if (!strcmp("-r", argv[i])) + ShowEffect = FALSE; + else if (!strcmp("-l", argv[i])) { + if (++i >= argc) + usage(); + PagerMax = atoi(argv[i]); + } +#ifdef JP_CHARSET +#ifndef DEBIAN /* XXX: use -o kanjicode={S|J|E} */ + else if (!strcmp("-s", argv[i])) + DisplayCode = CODE_SJIS; + else if (!strcmp("-j", argv[i])) + DisplayCode = CODE_JIS_n; + else if (!strcmp("-e", argv[i])) + DisplayCode = CODE_EUC; +#endif + else if (!strcmp("-I", argv[i])) { + if (++i >= argc) + usage(); + DocumentCode = str_to_code(argv[i]); + } +#endif /* JP_CHARSET */ +#ifndef KANJI_SYMBOLS + else if (!strcmp("-no-graph", argv[i])) + no_graphic_char = TRUE; +#endif /* not KANJI_SYMBOLS */ + else if (!strcmp("-T", argv[i])) { + if (++i >= argc) + usage(); + DefaultType = argv[i]; + } + else if (!strcmp("-m", argv[i])) + SearchHeader = TRUE; + else if (!strcmp("-v", argv[i])) + visual_start = TRUE; +#ifdef COLOR + else if (!strcmp("-M", argv[i])) + useColor = FALSE; +#endif /* COLOR */ + else if (!strcmp("-B", argv[i])) + load_bookmark = TRUE; + else if (!strcmp("-bookmark", argv[i])) { + if (++i >= argc) + usage(); + BookmarkFile = argv[i]; + if (BookmarkFile[0] != '~' && BookmarkFile[0] != '/') { + Str tmp = Strnew_charp(CurrentDir); + if (Strlastchar(tmp) != '/') + Strcat_char(tmp, '/'); + Strcat_charp(tmp, BookmarkFile); + BookmarkFile = cleanupName(tmp->ptr); + } + } + else if (!strcmp("-F", argv[i])) + RenderFrame = TRUE; + else if (!strcmp("-W", argv[i])) { + if (WrapSearch) { + WrapSearch = FALSE; + } + else { + WrapSearch = TRUE; + } + } + else if (!strcmp("-dump", argv[i])) { + w3m_dump = TRUE; + w3m_dump_source = FALSE; + w3m_dump_head = FALSE; + w3m_halfdump = FALSE; + if (COLS == 0) + COLS = 80; + } + else if (!strcmp("-dump_source", argv[i])) { + w3m_dump = TRUE; + w3m_dump_source = TRUE; + w3m_dump_head = FALSE; + w3m_halfdump = FALSE; + if (COLS == 0) + COLS = 80; + } + else if (!strcmp("-dump_head", argv[i])) { + w3m_dump = TRUE; + w3m_dump_head = TRUE; + w3m_dump_source = FALSE; + w3m_halfdump = FALSE; + if (COLS == 0) + COLS = 80; + } + else if (!strcmp("-halfdump", argv[i])) { + w3m_halfdump = TRUE; + w3m_dump = FALSE; + if (COLS == 0) + COLS = 80; + } + else if (!strcmp("-halfload", argv[i])) { + w3m_halfload = TRUE; + w3m_dump = FALSE; + w3m_halfdump = FALSE; + } + else if (!strcmp("-backend", argv[i])) { + w3m_backend = TRUE; + } + else if (!strcmp("-backend_batch", argv[i])) { + w3m_backend = TRUE; + if (++i >= argc) + usage(); + if (!backend_batch_commands) + backend_batch_commands = newTextList(); + pushText(backend_batch_commands, argv[i]); + } + else if (!strcmp("-cols", argv[i])) { + if (++i >= argc) + usage(); + COLS = atoi(argv[i]); + } + else if (!strcmp("-ppc", argv[i])) { + double ppc; + if (++i >= argc) + usage(); + ppc = atof(argv[i]); + if (ppc >= MINIMUM_PIXEL_PER_CHAR && + ppc <= MAXIMUM_PIXEL_PER_CHAR) + pixel_per_char = ppc; + } + else if (!strcmp("-num", argv[i])) + showLineNum = TRUE; + else if (!strcmp("-no-proxy", argv[i])) + Do_not_use_proxy = TRUE; +#ifdef MOUSE + else if (!strcmp("-no-mouse", argv[i])) { + mouse_end(); + use_mouse = FALSE; + } +#endif /* MOUSE */ +#ifdef USE_COOKIE + else if (!strcmp("-no-cookie", argv[i])) { + use_cookie = FALSE; + } + else if (!strcmp("-cookie", argv[i])) { + use_cookie = TRUE; + } + else if (!strcmp("-pauth", argv[i])) { + if (++i >= argc) + usage(); + proxy_auth_cookie = encodeB(argv[i]); + while (argv[i][0]) { + argv[i][0] = '\0'; + argv[i]++; + } + } +#endif /* USE_COOKIE */ +#ifdef DEBIAN + else if (!strcmp("-s", argv[i])) +#else + else if (!strcmp("-S", argv[i])) +#endif + squeezeBlankLine = TRUE; + else if (!strcmp("-X", argv[i])) + Do_not_use_ti_te = TRUE; + else if (!strcmp("-o", argv[i])) { +#ifdef SHOW_PARAMS + if (++i >= argc || !strcmp(argv[i], "?")) { + show_params_p = 1; + usage(); + } +#else + if (++i >= argc) + usage(); +#endif + if (!set_param_option(argv[i])) { + /* option set failed */ + fprintf(stderr, "%s: bad option\n", argv[i]); +#ifdef SHOW_PARAMS + show_params_p = 1; + usage(); +#else + exit(1); +#endif + } + option_assigned = 1; + } + else if (!strcmp("-dummy", argv[i])) { + /* do nothing */ + } + else if (!strcmp("-debug", argv[i])) + w3m_debug = TRUE; + else { + usage(); + } + } + else if (*argv[i] == '+') { + line_str = argv[i] + 1; + } + else { + load_argv[load_argc++] = argv[i]; + } + i++; + } + + if (option_assigned) { + parse_proxy(); + } + +#ifdef __WATT32__ + if (w3m_debug) + dbug_init(); + sock_init(); +#endif + + Firstbuf = NULL; + Currentbuf = NULL; + CurrentKey = -1; + if (BookmarkFile == NULL) + BookmarkFile = rcFile(BOOKMARK); + + if (!isatty(1) && !w3m_dump && !w3m_halfdump) { + /* redirected output */ + w3m_dump = TRUE; + if (COLS == 0) + COLS = 80; + } + if (w3m_backend) + backend(); + if (!w3m_dump && !w3m_halfdump) + fmInit(); + orig_GC_warn_proc = GC_set_warn_proc(wrap_GC_warn_proc); + if (w3m_halfdump) + printf("<pre>\n"); + + if (load_argc == 0) { + if (!isatty(0)) { + redin = newFileStream(fdopen(dup(0), "rb"), (void (*)()) pclose); + newbuf = openGeneralPagerBuffer(redin); + dup2(1, 0); + } + else if (load_bookmark) { + newbuf = loadGeneralFile(BookmarkFile, NULL, NO_REFERER, 0, NULL); + if (newbuf == NULL) + fprintf(stderr, "w3m: Can't load bookmark\n"); + } + else if (visual_start) { + Str s_page; + s_page = Strnew_charp("<title>W3M startup page</title><center><b>Welcome to "); +#ifdef JP_CHARSET + Strcat_charp(s_page, "<a href='http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/'>"); +#else + Strcat_charp(s_page, "<a href='http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/'>"); +#endif /* JP_CHARSET */ + Strcat_m_charp(s_page, + "w3m</a>!<p><p>This is w3m version ", + version, + "<br>Written by <a href='mailto:aito@ei5sun.yz.yamagata-u.ac.jp'>Akinori Ito</a>", + NULL); +#ifdef DEBIAN + Strcat_m_charp(s_page, + "<p>Debian package is maintained by <a href='mailto:ukai@debian.or.jp'>Fumitoshi UKAI</a>.", + "You can read <a href='file:///usr/share/doc/w3m/'>w3m documents on your local system</a>.", + NULL); +#endif /* DEBIAN */ + newbuf = loadHTMLString(s_page); + if (newbuf == NULL) + fprintf(stderr, "w3m: Can't load string\n"); + else + newbuf->bufferprop |= (BP_INTERNAL | BP_NO_URL); + } + else if ((p = getenv("HTTP_HOME")) != NULL || + (p = getenv("WWW_HOME")) != NULL) { + newbuf = loadGeneralFile(p, NULL, NO_REFERER, 0, NULL); + if (newbuf == NULL) + fprintf(stderr, "w3m: Can't load %s\n", p); + } + else { + if (fmInitialized) + fmTerm(); + usage(); + } + if (newbuf == NULL) { + deleteFiles(); + if (fmInitialized) { + sleep_till_anykey(1, 0); + fmTerm(); + } + exit(2); + } + i = -1; + } + else { + i = 0; + } + for (; i < load_argc; i++) { + if (i >= 0) { + if (w3m_dump && w3m_dump_head) { + request = New(FormList); + request->method = FORM_METHOD_HEAD; + newbuf = loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0, request); + } + else { + newbuf = loadGeneralFile(load_argv[i], NULL, NO_REFERER, 0, NULL); + } + if (newbuf == NULL) { + fprintf(stderr, "w3m: Can't load %s\n", load_argv[i]); + continue; + } + else if (newbuf == NO_BUFFER) + continue; + switch (newbuf->real_scheme) { +#ifdef USE_NNTP + case SCM_NNTP: + case SCM_NEWS: +#endif /* USE_NNTP */ + case SCM_MAILTO: + break; + case SCM_LOCAL: + case SCM_LOCAL_CGI: + unshiftHist(LoadHist, load_argv[i]); + break; + default: + pushHashHist(URLHist, parsedURL2Str(&newbuf->currentURL)->ptr); + break; + } + } + if (newbuf == NO_BUFFER) + continue; + if (w3m_halfdump) { + Currentbuf = Firstbuf = newbuf; + printf("</pre><title>%s</title>\n", htmlquote_str(newbuf->buffername)); + deleteFiles(); + exit(0); + } + if (w3m_dump) { + Currentbuf = Firstbuf = newbuf; + if (w3m_dump_source) + dump_source(Currentbuf); + else if (w3m_dump_head) + dump_head(Currentbuf); + else { + if (Currentbuf->frameset != NULL && RenderFrame) + rFrame(); + saveBuffer(Currentbuf, stdout); + } + deleteFiles(); +#ifdef USE_COOKIES + save_cookies(); +#endif /* USE_COOKIES */ + exit(0); + } + if (Currentbuf == NULL) + Firstbuf = Currentbuf = newbuf; + else { + Currentbuf->nextBuffer = newbuf; + Currentbuf = newbuf; + } + if (Currentbuf && Currentbuf != NO_BUFFER && + RenderFrame && Currentbuf->frameset != NULL) { + rFrame(); + Currentbuf = newbuf; + } +#ifdef BUFINFO + saveBufferInfo(); +#endif + + } + if (!Firstbuf || Firstbuf == NO_BUFFER) { + if (newbuf == NO_BUFFER) { + if (fmInitialized) + inputStr("Hit any key to quit w3m:", ""); + quitfm(); + } + deleteFiles(); + if (fmInitialized) { + sleep_till_anykey(1, 0); + fmTerm(); + } + exit(2); + } + +#ifdef SIGWINCH + signal(SIGWINCH, resize_hook); +#else /* not SIGWINCH */ + setlinescols(); + setupscreen(); +#endif /* not SIGWINCH */ + Currentbuf = Firstbuf; + displayBuffer(Currentbuf, B_NORMAL); + onA(); + if (line_str) { + _goLine(line_str); + } + for (;;) { + /* event processing */ + if (n_event_queue > 0) { + for (i = 0; i < n_event_queue; i++) { + CurrentKey = -1; + CurrentKeyData = eventQueue[i].user_data; +#ifdef MENU + CurrentMenuData = NULL; +#endif + w3mFuncList[eventQueue[i].cmd].func(); + } + n_event_queue = 0; + } + CurrentKeyData = NULL; + /* get keypress event */ +#ifdef MOUSE + if (use_mouse) + mouse_active(); +#endif /* MOUSE */ + c = getch(); +#ifdef MOUSE + if (use_mouse) + mouse_inactive(); +#endif /* MOUSE */ + if (IS_ASCII(c)) { /* Ascii */ + if (((prec_num && c == '0') || '1' <= c) && (c <= '9')) { + prec_num = prec_num * 10 + (int) (c - '0'); + if (prec_num > PREC_LIMIT) + prec_num = PREC_LIMIT; + } + else { + keyPressEventProc((int) c); + prec_num = 0; + } + } + prev_key = CurrentKey; + CurrentKey = -1; + } +} + +static void +keyPressEventProc(int c) +{ + CurrentKey = c; + w3mFuncList[(int) GlobalKeymap[c]].func(); + onA(); +} + +void +pushEvent(int event, void *user_data) +{ + if (n_event_queue < N_EVENT_QUEUE) { + eventQueue[n_event_queue].cmd = event; + eventQueue[n_event_queue].user_data = user_data; + n_event_queue++; + } +} + +static void +dump_source(Buffer * buf) +{ + FILE *f; + char c; + if (buf->sourcefile == NULL) + return; + f = fopen(buf->sourcefile, "r"); + if (f == NULL) + return; + while (c = fgetc(f), !feof(f)) { + putchar(c); + } + fclose(f); +} + +static void +dump_head(Buffer * buf) +{ + TextListItem *ti; + + if (buf->document_header == NULL) + return; + for (ti = buf->document_header->first; ti; ti = ti->next) { + printf("%s", ti->ptr); + } + puts(""); +} + +void +nulcmd(void) +{ /* do nothing */ +} + +#ifdef __EMX__ +void +pcmap(void) +{ + w3mFuncList[(int) PcKeymap[(int) getch()].func(); +} +#else /* not __EMX__ */ +void pcmap(void) +{ +} +#endif + +void +escmap(void) +{ + char c; + c = getch(); + if (IS_ASCII(c)) { + CurrentKey = K_ESC | c; + w3mFuncList[(int) EscKeymap[(int) c]].func(); + } +} + +void +escbmap(void) +{ + char c; + c = getch(); + + if (IS_DIGIT(c)) + escdmap(c); + else if (IS_ASCII(c)) { + CurrentKey = K_ESCB | c; + w3mFuncList[(int) EscBKeymap[(int) c]].func(); + } +} + +void +escdmap(char c) +{ + int d; + + d = (int) c - (int) '0'; + c = getch(); + if (IS_DIGIT(c)) { + d = d * 10 + (int) c - (int) '0'; + c = getch(); + } + if (c == '~') { + CurrentKey = K_ESCD | d; + w3mFuncList[(int) EscDKeymap[d]].func(); + } +} + +void +tmpClearBuffer(Buffer * buf) +{ + if (buf->pagerSource == NULL && + writeBufferCache(buf) == 0) { + buf->firstLine = NULL; + buf->topLine = NULL; + buf->currentLine = NULL; + buf->lastLine = NULL; + } +} + +static Str currentURL(void); + +void +saveBufferInfo() +{ + FILE *fp; + if ((fp = fopen(rcFile("bufinfo"), "w")) == NULL) { + return; + } + else { + fprintf(fp, "%s\n", currentURL()->ptr); + fclose(fp); + } +} + + +static void +pushBuffer(Buffer * buf) +{ + Buffer *b; + + if (clear_buffer) + tmpClearBuffer(Currentbuf); + if (Firstbuf == Currentbuf) { + buf->nextBuffer = Firstbuf; + Firstbuf = Currentbuf = buf; + } + else if ((b = prevBuffer(Firstbuf, Currentbuf)) != NULL) { + b->nextBuffer = buf; + buf->nextBuffer = Currentbuf; + Currentbuf = buf; + } +#ifdef BUFINFO + saveBufferInfo(); +#endif + +} + +static void +delBuffer(Buffer * buf) +{ + if (buf == NULL) + return; + if (Currentbuf == buf) + Currentbuf = buf->nextBuffer; + Firstbuf = deleteBuffer(Firstbuf, buf); + if (!Currentbuf) + Currentbuf = Firstbuf; +} + +static void +repBuffer(Buffer * oldbuf, Buffer * buf) +{ + Firstbuf = replaceBuffer(Firstbuf, oldbuf, buf); + Currentbuf = buf; +} + + +MySignalHandler +intTrap(SIGNAL_ARG) +{ /* Interrupt catcher */ + LONGJMP(IntReturn, 0); + SIGNAL_RETURN; +} + +#ifdef SIGWINCH +MySignalHandler +resize_hook(SIGNAL_ARG) +{ + setlinescols(); + setupscreen(); + if (Currentbuf) + displayBuffer(Currentbuf, B_FORCE_REDRAW); + signal(SIGWINCH, resize_hook); + SIGNAL_RETURN; +} +#endif /* SIGWINCH */ + +/* + * Command functions: These functions are called with a keystroke. + */ + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +static void +nscroll(int n, int mode) +{ + Line *curtop = Currentbuf->topLine; + int lnum, tlnum, llnum, diff_n; + + if (Currentbuf->firstLine == NULL) + return; + lnum = Currentbuf->currentLine->linenumber; + Currentbuf->topLine = lineSkip(Currentbuf, curtop, n, FALSE); + if (Currentbuf->topLine == curtop) { + lnum += n; + if (lnum < Currentbuf->topLine->linenumber) + lnum = Currentbuf->topLine->linenumber; + else if (lnum > Currentbuf->lastLine->linenumber) + lnum = Currentbuf->lastLine->linenumber; + } else { + tlnum = Currentbuf->topLine->linenumber; + llnum = Currentbuf->topLine->linenumber + LASTLINE - 1; + diff_n = n - (tlnum - curtop->linenumber); + if (lnum < tlnum) + lnum = tlnum + diff_n; + if (lnum > llnum) + lnum = llnum + diff_n; + } + gotoLine(Currentbuf, lnum); + arrangeLine(Currentbuf); + displayBuffer(Currentbuf, mode); +} + +/* Move page forward */ +void +pgFore(void) +{ +#ifdef VI_PREC_NUM + nscroll(PREC_NUM * (LASTLINE - 1), B_NORMAL); +#else /* not VI_PREC_NUM */ + nscroll(prec_num ? prec_num : (LASTLINE - 1), + prec_num ? B_SCROLL : B_NORMAL); +#endif /* not VI_PREC_NUM */ +} + +/* Move page backward */ +void +pgBack(void) +{ +#ifdef VI_PREC_NUM + nscroll(- PREC_NUM * (LASTLINE - 1), B_NORMAL); +#else /* not VI_PREC_NUM */ + nscroll(- (prec_num ? prec_num : (LASTLINE - 1)), + prec_num ? B_SCROLL : B_NORMAL); +#endif /* not VI_PREC_NUM */ +} + +/* 1 line up */ +void +lup1(void) +{ + nscroll(PREC_NUM, B_SCROLL); +} + +/* 1 line down */ +void +ldown1(void) +{ + nscroll(-PREC_NUM, B_SCROLL); +} + +/* move cursor position to the center of screen */ +void +ctrCsrV(void) +{ + int offsety; + if (Currentbuf->firstLine == NULL) + return; + offsety = LASTLINE / 2 - Currentbuf->cursorY; + if (offsety != 0) { +/* Currentbuf->currentLine = lineSkip(Currentbuf, + * Currentbuf->currentLine,offsety, FALSE); */ + Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine, -offsety, FALSE); + arrangeLine(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); + } +} + +void +ctrCsrH(void) +{ + int offsetx; + if (Currentbuf->firstLine == NULL) + return; + offsetx = Currentbuf->cursorX - COLS / 2; + if (offsetx != 0) { + columnSkip(Currentbuf, offsetx); + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); + } +} + +/* Redraw screen */ +void +rdrwSc(void) +{ + clear(); + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Search regular expression forward */ +void +srchfor(void) +{ + MySignalHandler(*prevtrap) (); + char *str; + int i; + int wrapped = 0; + + str = inputStrHist("Forward: ", NULL, TextHist); + if (str != NULL && *str == '\0') + str = SearchString; + if (str == NULL || *str == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + SearchString = str; + prevtrap = signal(SIGINT, intTrap); + crmode(); + if (SETJMP(IntReturn) == 0) + for (i = 0; i < PREC_NUM; i++) + wrapped = forwardSearch(Currentbuf, SearchString); + signal(SIGINT, prevtrap); + term_raw(); + displayBuffer(Currentbuf, B_NORMAL); + if (wrapped) { + disp_message("Search wrapped", FALSE); + } + searchRoutine = forwardSearch; +} + +/* Search regular expression backward */ +void +srchbak(void) +{ + MySignalHandler(*prevtrap) (); + char *str; + int i; + int wrapped = 0; + + str = inputStrHist("Backward: ", NULL, TextHist); + if (str != NULL && *str == '\0') + str = SearchString; + if (str == NULL || *str == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + SearchString = str; + prevtrap = signal(SIGINT, intTrap); + crmode(); + if (SETJMP(IntReturn) == 0) + for (i = 0; i < PREC_NUM; i++) + wrapped = backwardSearch(Currentbuf, SearchString); + signal(SIGINT, prevtrap); + term_raw(); + displayBuffer(Currentbuf, B_NORMAL); + if (wrapped) { + disp_message("Search wrapped", FALSE); + } + searchRoutine = backwardSearch; +} + +static void +srch_nxtprv(int reverse) +{ + int i; + int wrapped = 0; + static int (*routine[2]) (Buffer *, char *) = + { + forwardSearch, backwardSearch + }; + MySignalHandler(*prevtrap) (); + + if (searchRoutine == NULL) { + disp_message("No previous regular expression", TRUE); + return; + } + move(LASTLINE, 0); + addstr(searchRoutine == forwardSearch ? "Forward: " : "Backward: "); + addstr(SearchString); + clrtoeolx(); + move(LASTLINE, 0); + refresh(); + if (reverse != 0) + reverse = 1; + if (searchRoutine == backwardSearch) + reverse ^= 1; + prevtrap = signal(SIGINT, intTrap); + crmode(); + if (SETJMP(IntReturn) == 0) + for (i = 0; i < PREC_NUM; i++) + wrapped = (*routine[reverse]) (Currentbuf, SearchString); + signal(SIGINT, prevtrap); + term_raw(); + displayBuffer(Currentbuf, B_NORMAL); + if (wrapped) { + disp_message("Search wrapped", FALSE); + } +} + +/* Search next matching */ +void +srchnxt(void) +{ + srch_nxtprv(0); +} + +/* Search previous matching */ +void +srchprv(void) +{ + srch_nxtprv(1); +} + +static void +shiftvisualpos(Buffer * buf, int shift) +{ + buf->visualpos -= shift; + if (buf->visualpos >= COLS) + buf->visualpos = COLS - 1; + else if (buf->visualpos < 0) + buf->visualpos = 0; + arrangeLine(buf); + if (buf->visualpos == -shift && buf->cursorX == 0) + buf->visualpos = 0; +} + +/* Shift screen left */ +void +shiftl(void) +{ + int column; + + if (Currentbuf->firstLine == NULL) + return; + column = Currentbuf->currentColumn; + columnSkip(Currentbuf, PREC_NUM * (-COLS + 1) + 1); + shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Shift screen right */ +void +shiftr(void) +{ + int column; + + if (Currentbuf->firstLine == NULL) + return; + column = Currentbuf->currentColumn; + columnSkip(Currentbuf, PREC_NUM * (COLS - 1) - 1); + shiftvisualpos(Currentbuf, Currentbuf->currentColumn - column); + displayBuffer(Currentbuf, B_NORMAL); +} + +void +col1R(void) +{ + Buffer *buf = Currentbuf; + Line *l = buf->currentLine; + int j, column; + + if (l == NULL) + return; + for (j = 0; j < PREC_NUM; j++) { + column = buf->currentColumn; + columnSkip(Currentbuf, 1); + if (column == buf->currentColumn) + break; + shiftvisualpos(Currentbuf, 1); + } + displayBuffer(Currentbuf, B_NORMAL); +} + +void +col1L(void) +{ + Buffer *buf = Currentbuf; + Line *l = buf->currentLine; + int j; + + if (l == NULL) + return; + for (j = 0; j < PREC_NUM; j++) { + if (buf->currentColumn == 0) + break; + columnSkip(Currentbuf, -1); + shiftvisualpos(Currentbuf, -1); + } + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Execute shell command and read output ac pipe. */ +void +pipesh(void) +{ + Buffer *buf; + char *cmd; + + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + cmd = searchKeyData(); + if (cmd == NULL || *cmd == '\0') { + cmd = inputLineHist("(read shell[pipe])!", "", IN_COMMAND, ShellHist); + if (cmd == NULL || *cmd == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + } + buf = getpipe(cmd); + if (buf == NULL) { + disp_message("Execution failed", FALSE); + } + else { + buf->bufferprop |= (BP_INTERNAL | BP_NO_URL); + if (buf->type == NULL) + buf->type = "text/plain"; + pushBuffer(buf); + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Execute shell command and load entire output to buffer */ +void +readsh(void) +{ + Buffer *buf; + MySignalHandler(*prevtrap) (); + char *cmd; + + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + cmd = searchKeyData(); + if (cmd == NULL || *cmd == '\0') { + cmd = inputLineHist("(read shell)!", "", IN_COMMAND, ShellHist); + if (cmd == NULL || *cmd == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + } + prevtrap = signal(SIGINT, intTrap); + crmode(); + buf = getshell(cmd); + signal(SIGINT, prevtrap); + term_raw(); + if (buf == NULL) { + disp_message("Execution failed", FALSE); + } + else { + buf->bufferprop |= (BP_INTERNAL | BP_NO_URL); + if (buf->type == NULL) + buf->type = "text/plain"; + pushBuffer(buf); + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Execute shell command */ +void +execsh(void) +{ +#ifdef MOUSE + int use_m = use_mouse; +#endif + char *cmd; + + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + cmd = searchKeyData(); + if (cmd == NULL || *cmd == '\0') { + cmd = inputLineHist("(exec shell)!", "", IN_COMMAND, ShellHist); + } + if (cmd != NULL && *cmd != '\0') { + fmTerm(); + system(cmd); + printf("\n[Hit any key]"); + fflush(stdout); +#ifdef MOUSE + use_mouse = FALSE; +#endif + fmInit(); + getch(); +#ifdef MOUSE + use_mouse = use_m; + if (use_mouse) + mouse_init(); +#endif + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Load file */ +void +ldfile(void) +{ + char *fn; + + fn = searchKeyData(); + if (fn == NULL || *fn == '\0') { + fn = inputFilenameHist("(Load)Filename? ", NULL, LoadHist); + if (fn == NULL || *fn == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + } + cmd_loadfile(fn); +} + +/* Load help file */ +void +ldhelp(void) +{ + cmd_loadURL(helpFile(HELP_FILE), NULL); +} + +static void +cmd_loadfile(char *fn) +{ + Buffer *buf; + + buf = loadGeneralFile(fn, NULL, NO_REFERER, 0, NULL); + if (buf == NULL) { + char *emsg = Sprintf("%s not found", fn)->ptr; + disp_err_message(emsg, FALSE); + } + else if (buf != NO_BUFFER) { + pushBuffer(buf); + if (RenderFrame && Currentbuf->frameset != NULL) + rFrame(); + } + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Move cursor left */ +void +movL(void) +{ + int i; + if (Currentbuf->firstLine == NULL) + return; + for (i = 0; i < PREC_NUM; i++) + cursorLeft(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Move cursor downward */ +void +movD(void) +{ + int i; + if (Currentbuf->firstLine == NULL) + return; + for (i = 0; i < PREC_NUM; i++) + cursorDown(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* move cursor upward */ +void +movU(void) +{ + int i; + if (Currentbuf->firstLine == NULL) + return; + for (i = 0; i < PREC_NUM; i++) + cursorUp(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Move cursor right */ +void +movR(void) +{ + int i; + if (Currentbuf->firstLine == NULL) + return; + for (i = 0; i < PREC_NUM; i++) + cursorRight(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + + + +/* movLW, movRW */ +/* + * From: Takashi Nishimoto <g96p0935@mse.waseda.ac.jp> Date: Mon, 14 Jun + * 1999 09:29:56 +0900 */ + +#define IS_WORD_CHAR(c,p) (IS_ALNUM(c) && CharType(p) == PC_ASCII) + +static int +prev_nonnull_line(Line * line) +{ + Line *l; + + for (l = line; l != NULL && l->len == 0; l = l->prev); + if (l == NULL || l->len == 0) + return -1; + + Currentbuf->currentLine = l; + if (l != line) + Currentbuf->pos = Currentbuf->currentLine->len; + return 0; +} + +void +movLW(void) +{ + char *lb; + Lineprop *pb; + Line *pline; + int ppos; + int i; + + if (Currentbuf->firstLine == NULL) + return; + + for (i = 0; i < PREC_NUM; i++) { + pline = Currentbuf->currentLine; + ppos = Currentbuf->pos; + + if (prev_nonnull_line(Currentbuf->currentLine) < 0) + goto end; + + while (1) { + lb = Currentbuf->currentLine->lineBuf; + pb = Currentbuf->currentLine->propBuf; + while (Currentbuf->pos > 0 && + !IS_WORD_CHAR(lb[Currentbuf->pos - 1], pb[Currentbuf->pos - 1])) { + Currentbuf->pos--; + } + if (Currentbuf->pos > 0) + break; + if (prev_nonnull_line(Currentbuf->currentLine->prev) < 0) { + Currentbuf->currentLine = pline; + Currentbuf->pos = ppos; + goto end; + } + Currentbuf->pos = Currentbuf->currentLine->len; + } + + lb = Currentbuf->currentLine->lineBuf; + pb = Currentbuf->currentLine->propBuf; + while (Currentbuf->pos > 0 && + IS_WORD_CHAR(lb[Currentbuf->pos - 1], pb[Currentbuf->pos - 1])) { + Currentbuf->pos--; + } + } + end: + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +static int +next_nonnull_line(Line * line) +{ + Line *l; + + for (l = line; l != NULL && l->len == 0; l = l->next); + + if (l == NULL || l->len == 0) + return -1; + + Currentbuf->currentLine = l; + if (l != line) + Currentbuf->pos = 0; + return 0; +} + +void +movRW(void) +{ + char *lb; + Lineprop *pb; + Line *pline; + int ppos; + int i; + + if (Currentbuf->firstLine == NULL) + return; + + for (i = 0; i < PREC_NUM; i++) { + pline = Currentbuf->currentLine; + ppos = Currentbuf->pos; + + if (next_nonnull_line(Currentbuf->currentLine) < 0) + goto end; + + lb = Currentbuf->currentLine->lineBuf; + pb = Currentbuf->currentLine->propBuf; + + while (lb[Currentbuf->pos] && + IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos])) + Currentbuf->pos++; + + while (1) { + while (lb[Currentbuf->pos] && + !IS_WORD_CHAR(lb[Currentbuf->pos], pb[Currentbuf->pos])) + Currentbuf->pos++; + if (lb[Currentbuf->pos]) + break; + if (next_nonnull_line(Currentbuf->currentLine->next) < 0) { + Currentbuf->currentLine = pline; + Currentbuf->pos = ppos; + goto end; + } + Currentbuf->pos = 0; + lb = Currentbuf->currentLine->lineBuf; + pb = Currentbuf->currentLine->propBuf; + } + } + end: + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Question and Quit */ +void +qquitfm(void) +{ + char *ans; + if (!confirm_on_quit) + quitfm(); + ans = inputStr("Do you want to exit w3m? (y or n)", ""); + if (ans != NULL && tolower(*ans) == 'y') + quitfm(); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Quit */ +void +quitfm(void) +{ + fmTerm(); + deleteFiles(); +#ifdef USE_COOKIE + save_cookies(); +#endif /* USE_COOKIE */ +#ifdef USE_HISTORY + if (SaveURLHist) + saveHistory(URLHist, URLHistSize); +#endif /* USE_HISTORY */ + exit(0); +} + +/* Select buffer */ +void +selBuf(void) +{ + Buffer *buf; + int ok; + char cmd; + + ok = FALSE; + do { + buf = selectBuffer(Firstbuf, Currentbuf, &cmd); + switch (cmd) { + case 'B': + ok = TRUE; + break; + case '\n': + case ' ': + Currentbuf = buf; + ok = TRUE; + break; + case 'D': + delBuffer(buf); + if (Firstbuf == NULL) { + /* No more buffer */ + Firstbuf = nullBuffer(); + Currentbuf = Firstbuf; + } + break; + case 'q': + qquitfm(); + break; + case 'Q': + quitfm(); + break; + } + } while (!ok); + + if (clear_buffer) { + for (buf = Firstbuf; buf != NULL; buf = buf->nextBuffer) + tmpClearBuffer(buf); + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Suspend (on BSD), or run interactive shell (on SysV) */ +void +susp(void) +{ +#ifndef SIGSTOP + char *shell; +#endif /* not SIGSTOP */ + move(LASTLINE, 0); + clrtoeolx(); + refresh(); + fmTerm(); +#ifndef SIGSTOP + shell = getenv("SHELL"); + if (shell == NULL) + shell = "/bin/sh"; + system(shell); +#else /* SIGSTOP */ + kill((pid_t)0, SIGSTOP); +#endif /* SIGSTOP */ + fmInit(); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Go to specified line */ +static void +_goLine(char *l) +{ + if (l == NULL || *l == '\0' || Currentbuf->currentLine == NULL) { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + Currentbuf->pos = 0; + if (((*l == '^') || (*l == '$')) && prec_num) { + gotoRealLine(Currentbuf, prec_num); + } + else if (*l == '^') { + Currentbuf->topLine = Currentbuf->currentLine = Currentbuf->firstLine; + } + else if (*l == '$') { + Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->lastLine, -(LASTLINE + 1) / 2, TRUE); + Currentbuf->currentLine = Currentbuf->lastLine; + } + else + gotoRealLine(Currentbuf, atoi(l)); + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +goLine(void) +{ + char *l = inputStr("Goto line: ", ""); + prec_num = 0; + _goLine(l); +} + +void +goLineF(void) +{ + _goLine("^"); +} + +void +goLineL(void) +{ + _goLine("$"); +} + +/* Go to the beginning of the line */ +void +linbeg(void) +{ + if (Currentbuf->firstLine == NULL) + return; + Currentbuf->pos = 0; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Go to the bottom of the line */ +void +linend(void) +{ + if (Currentbuf->firstLine == NULL) + return; + Currentbuf->pos = Currentbuf->currentLine->len - 1; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* Run editor on the current buffer */ +void +editBf(void) +{ + char *fn = Currentbuf->filename; + char *type = Currentbuf->type; + int top, linenum, cursorY, pos, currentColumn; + Buffer *buf, *fbuf = NULL; + Str cmd; + + if (fn == NULL || + Currentbuf->pagerSource != NULL || /* Behaving as a pager */ + (Currentbuf->type == NULL && + Currentbuf->edit == NULL) || /* Reading shell */ + Currentbuf->real_scheme != SCM_LOCAL || + !strcmp(Currentbuf->currentURL.file, "-") || /* file is std * + * input */ + Currentbuf->bufferprop & BP_FRAME) { /* Frame */ + disp_err_message("Can't edit other than local file", TRUE); + return; + } + if (Currentbuf->frameset != NULL) + fbuf = Currentbuf->linkBuffer[LB_FRAME]; + if (Currentbuf->firstLine == NULL) { + top = 1; + linenum = 1; + } else { + top = Currentbuf->topLine->linenumber; + linenum = Currentbuf->currentLine->linenumber; + } + cursorY = Currentbuf->cursorY; + pos = Currentbuf->pos; + currentColumn = Currentbuf->currentColumn; + if (Currentbuf->edit) { + cmd = unquote_mailcap(Currentbuf->edit, + Currentbuf->real_type, + quoteShell(fn)->ptr, NULL); + } + else { + char *file = quoteShell(fn)->ptr; + if (strcasestr(Editor, "%s")) { + if (strcasestr(Editor, "%d")) + cmd = Sprintf(Editor, linenum, file); + else + cmd = Sprintf(Editor, file); + } else { + if (strcasestr(Editor, "%d")) + cmd = Sprintf(Editor, linenum); + else if (strcasestr(Editor, "vi")) + cmd = Sprintf("%s +%d", Editor, linenum); + else + cmd = Strnew_charp(Editor); + Strcat_m_charp(cmd, " ", file, NULL); + } + } + fmTerm(); + system(cmd->ptr); + fmInit(); + buf = loadGeneralFile(fn, NULL, NO_REFERER, 0, NULL); + if (buf == NULL) { + disp_err_message("Re-loading failed", FALSE); + buf = nullBuffer(); + } + else if (buf == NO_BUFFER) { + buf = nullBuffer(); + } + if (fbuf != NULL) + Firstbuf = deleteBuffer(Firstbuf, fbuf); + repBuffer(Currentbuf, buf); + if ((type != NULL) && (buf->type != NULL) && + ((!strcasecmp(buf->type, "text/plain") && + !strcasecmp(type, "text/html")) || + (!strcasecmp(buf->type, "text/html") && + !strcasecmp(type, "text/plain")))) { + vwSrc(); + if (Currentbuf != buf) + Firstbuf = deleteBuffer(Firstbuf, buf); + } + if (Currentbuf->firstLine == NULL) { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->topLine, top - 1, FALSE); + gotoLine(Currentbuf, linenum); + Currentbuf->pos = pos; + Currentbuf->currentColumn = currentColumn; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* Run editor on the current screen */ +void +editScr(void) +{ + int lnum; + Str cmd; + Str tmpf; + FILE *f; + + tmpf = tmpfname(TMPF_DFL, NULL); + f = fopen(tmpf->ptr, "w"); + if (f == NULL) { + cmd = Sprintf("Can't open %s\n", tmpf->ptr); + disp_err_message(cmd->ptr, TRUE); + return; + } + saveBuffer(Currentbuf, f); + fclose(f); + if (Currentbuf->currentLine) + lnum = Currentbuf->currentLine->linenumber; + else + lnum = 1; + if (strcasestr(Editor, "%s")) { + if (strcasestr(Editor, "%d")) + cmd = Sprintf(Editor, lnum, tmpf->ptr); + else + cmd = Sprintf(Editor, tmpf->ptr); + } else { + if (strcasestr(Editor, "%d")) + cmd = Sprintf(Editor, lnum); + else if (strcasestr(Editor, "vi")) + cmd = Sprintf("%s +%d", Editor, lnum); + else + cmd = Strnew_charp(Editor); + Strcat_m_charp(cmd, " ", tmpf->ptr, NULL); + } + fmTerm(); + system(cmd->ptr); + fmInit(); + unlink(tmpf->ptr); + gotoLine(Currentbuf, lnum); + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +#ifdef USE_MARK + +/* Set / unset mark */ +void +_mark(void) +{ + Line *l; + if (Currentbuf->firstLine == NULL) + return; + l = Currentbuf->currentLine; + cmd_mark(&l->propBuf[Currentbuf->pos]); + redrawLine(Currentbuf, l, l->linenumber - Currentbuf->topLine->linenumber); +} + +static void +cmd_mark(Lineprop * p) +{ + if ((*p & PM_MARK) && (*p & PE_STAND)) + *p &= ~PE_STAND; + else if (!(*p & PM_MARK) && !(*p & PE_STAND)) + *p |= PE_STAND; + *p ^= PM_MARK; +} + +/* Go to next mark */ +void +nextMk(void) +{ + Line *l; + int i; + + if (Currentbuf->firstLine == NULL) + return; + i = Currentbuf->pos + 1; + l = Currentbuf->currentLine; + if (i >= l->len) { + i = 0; + l = l->next; + } + while (l != NULL) { + for (; i < l->len; i++) { + if (l->propBuf[i] & PM_MARK) { + Currentbuf->currentLine = l; + Currentbuf->pos = i; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); + return; + } + } + l = l->next; + i = 0; + } + disp_message("No mark exist after here", TRUE); +} + +/* Go to previous mark */ +void +prevMk(void) +{ + Line *l; + int i; + + if (Currentbuf->firstLine == NULL) + return; + i = Currentbuf->pos - 1; + l = Currentbuf->currentLine; + if (i < 0) { + l = l->prev; + if (l != NULL) + i = l->len - 1; + } + while (l != NULL) { + for (; i >= 0; i--) { + if (l->propBuf[i] & PM_MARK) { + Currentbuf->currentLine = l; + Currentbuf->pos = i; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); + return; + } + } + l = l->prev; + if (l != NULL) + i = l->len - 1; + } + disp_message("No mark exist before here", TRUE); +} + +/* Mark place to which the regular expression matches */ +void +reMark(void) +{ + Line *l; + char *str; + char *p, *p1, *p2; + + str = inputStrHist("(Mark)Regexp: ", MarkString, TextHist); + if (str == NULL || *str == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + MarkString = str; + if ((MarkString = regexCompile(MarkString, 1)) != NULL) { + disp_message(MarkString, TRUE); + return; + } + for (l = Currentbuf->firstLine; l != NULL; l = l->next) { + p = l->lineBuf; + for (;;) { + if (regexMatch(p, &l->lineBuf[l->len] - p, p == l->lineBuf) == 1) { + matchedPosition(&p1, &p2); + cmd_mark(l->propBuf + (p1 - l->lineBuf)); + p = p2; + } + else + break; + } + } + + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} +#endif /* USE_MARK */ + +#ifdef JP_CHARSET +static char * +cURLcode(char *url, Buffer * buf) +{ + char *p; + Str s; + + for (p = url; *p; p++) { + if (!IS_ASCII(*p)) { + /* URL contains Kanji... uugh */ + s = conv(url, InnerCode, buf->document_code); + return s->ptr; + } + } + return url; +} +#else /* not JP_CHARSET */ +#define cURLcode(url,buf) (url) +#endif /* not JP_CHARSET */ + +static Buffer * +loadNormalBuf(Buffer * buf, int renderframe) +{ + pushBuffer(buf); + if (renderframe && RenderFrame && Currentbuf->frameset != NULL) + rFrame(); + return buf; +} + +static Buffer * +loadLink(char *url, char *target, char *referer, FormList * request) +{ + Buffer *buf, *nfbuf; + union frameset_element *f_element = NULL; + int flag = 0; + ParsedURL *base, pu; + + message(Sprintf("loading %s\n", url)->ptr, 0, 0); + refresh(); + + base = baseURL(Currentbuf); + if (base == NULL || + base->scheme == SCM_LOCAL || base->scheme == SCM_LOCAL_CGI) + referer = NO_REFERER; + if (referer == NULL) + referer = parsedURL2Str(&Currentbuf->currentURL)->ptr; + buf = loadGeneralFile(cURLcode(url, Currentbuf), + baseURL(Currentbuf), + referer, + flag, + request); + if (buf == NULL) { + char *emsg = Sprintf("Can't load %s\n", url)->ptr; + disp_err_message(emsg, FALSE); + return NULL; + } + + parseURL2(url, &pu, base); + pushHashHist(URLHist, parsedURL2Str(&pu)->ptr); + + if (buf == NO_BUFFER) { + return NULL; + } + if (!on_target) /* open link as an indivisual page */ + return loadNormalBuf(buf, TRUE); + + if (do_download) /* download (thus no need to render frame) */ + return loadNormalBuf(buf, FALSE); + + if (target == NULL || /* no target specified (that means * this + * page is not a frame page) */ + !strcmp(target, "_top") || /* this link is specified to * be + * opened as an indivisual * page */ + !(Currentbuf->bufferprop & BP_FRAME) /* This page is not a * + * frame page */ + ) { + return loadNormalBuf(buf, TRUE); + } + nfbuf = Currentbuf->linkBuffer[LB_N_FRAME]; + if (nfbuf == NULL) { + /* original page (that contains <frameset> tag) doesn't exist */ + return loadNormalBuf(buf, TRUE); + } + + f_element = search_frame(nfbuf->frameset, target); + if (f_element == NULL) { + /* specified target doesn't exist in this frameset */ + return loadNormalBuf(buf, TRUE); + } + + /* frame page */ + + /* stack current frameset */ + pushFrameTree(&(nfbuf->frameQ), + copyFrameSet(nfbuf->frameset), + Currentbuf->currentLine ? + Currentbuf->currentLine->linenumber : 0, + Currentbuf->pos); + /* delete frame view buffer */ + delBuffer(Currentbuf); + Currentbuf = nfbuf; + /* nfbuf->frameset = copyFrameSet(nfbuf->frameset); */ + resetFrameElement(f_element, buf, referer, request); + discardBuffer(buf); + rFrame(); + { + Anchor *al = NULL; + char *label = pu.label; + + if (label && f_element->element->attr == F_BODY) { + al = searchAnchor(f_element->body->nameList, label); + } + if (!al) { + label = Strnew_m_charp("_", target, NULL)->ptr; + al = searchURLLabel(Currentbuf, label); + } + if (al) { + gotoLine(Currentbuf, al->start.line); + Currentbuf->pos = al->start.pos; + arrangeCursor(Currentbuf); + } + } + displayBuffer(Currentbuf, B_NORMAL); + return buf; +} + +static void +gotoLabel(char *label) +{ + Buffer *buf; + Anchor *al; + int i; + + al = searchURLLabel(Currentbuf, label); + if (al == NULL) { + disp_message(Sprintf("%s is not found", label)->ptr, TRUE); + return; + } + buf = newBuffer(Currentbuf->width); + copyBuffer(buf, Currentbuf); + for (i = 0; i < MAX_LB; i++) + buf->linkBuffer[i] = NULL; + buf->currentURL.label = allocStr(label, 0); + pushHashHist(URLHist, parsedURL2Str(&buf->currentURL)->ptr); + (*buf->clone)++; + pushBuffer(buf); + gotoLine(Currentbuf, al->start.line); + Currentbuf->pos = al->start.pos; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; +} + +/* follow HREF link */ +void +followA(void) +{ + Line *l; + Anchor *a; + ParsedURL u; + + if (Currentbuf->firstLine == NULL) + return; + l = Currentbuf->currentLine; + + a = retrieveCurrentAnchor(Currentbuf); + if (a == NULL) { + followForm(); + return; + } + if (*a->url == '#') { /* index within this buffer */ + gotoLabel(a->url + 1); + return; + } + parseURL2(a->url, &u, baseURL(Currentbuf)); + if (u.scheme == Currentbuf->currentURL.scheme && + u.port == Currentbuf->currentURL.port && + ((u.host == NULL && Currentbuf->currentURL.host == NULL) || + (u.host != NULL && Currentbuf->currentURL.host != NULL && + strcasecmp(u.host, Currentbuf->currentURL.host) == 0)) && + ((u.file == NULL && Currentbuf->currentURL.file == NULL) || + (u.file != NULL && Currentbuf->currentURL.file != NULL && + strcmp(u.file, Currentbuf->currentURL.file) == 0))) { + /* index within this buffer */ + if (u.label) { + gotoLabel(u.label); + return; + } + } + if (!strncasecmp(a->url, "mailto:", 7)) { + /* invoke external mailer */ + Str tmp; + char *to = quoteShell(a->url + 7)->ptr; + if (strcasestr(Mailer, "%s")) + tmp = Sprintf(Mailer, to); + else + tmp = Strnew_m_charp(Mailer, " ", to, NULL); + fmTerm(); + system(tmp->ptr); + fmInit(); + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } +#ifdef USE_NNTP + else if (!strncasecmp(a->url, "news:", 5) && + strchr(a->url, '@') == NULL) { + /* news:newsgroup is not supported */ + disp_err_message("news:newsgroup_name is not supported", TRUE); + return; + } +#endif /* USE_NNTP */ + loadLink(a->url, a->target, a->referer, NULL); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* follow HREF link in the buffer */ +void +bufferA(void) +{ + on_target = FALSE; + followA(); + on_target = TRUE; +} + +/* view inline image */ +void +followI(void) +{ + Line *l; + Anchor *a; + Buffer *buf; + + if (Currentbuf->firstLine == NULL) + return; + l = Currentbuf->currentLine; + + a = retrieveCurrentImg(Currentbuf); + if (a == NULL) + return; + message(Sprintf("loading %s\n", a->url)->ptr, 0, 0); + refresh(); + buf = loadGeneralFile(cURLcode(a->url, Currentbuf), baseURL(Currentbuf), NULL, 0, NULL); + if (buf == NULL) { + char *emsg = Sprintf("Can't load %s\n", a->url)->ptr; + disp_err_message(emsg, FALSE); + } + else if (buf != NO_BUFFER) { + pushBuffer(buf); + } + displayBuffer(Currentbuf, B_NORMAL); +} + +static void +do_update_form_radio(FormItemList * f, void *data) +{ + Buffer *buf = (Buffer *) data; + formUpdateBuffer(&buf->formitem->anchors[f->anchor_num], buf, f); +} + +static FormItemList * +save_submit_formlist(FormItemList * src) +{ + FormList *list; + FormList *srclist; + FormItemList *srcitem; + FormItemList *item; + FormItemList *ret = NULL; +#ifdef MENU_SELECT + FormSelectOptionItem *opt; + FormSelectOptionItem *curopt; + FormSelectOptionItem *srcopt; +#endif /* MENU_SELECT */ + + if (src == NULL) + return NULL; + srclist = src->parent; + list = New(FormList); + list->method = srclist->method; + list->action = Strdup(srclist->action); + list->charset = srclist->charset; + list->enctype = srclist->enctype; + list->nitems = srclist->nitems; + list->body = srclist->body; + list->boundary = srclist->boundary; + list->length = srclist->length; + + for (srcitem = srclist->item; srcitem; srcitem = srcitem->next) { + item = New(FormItemList); + item->type = srcitem->type; + item->name = Strdup(srcitem->name); + item->value = Strdup(srcitem->value); + item->checked = srcitem->checked; + item->accept = srcitem->accept; + item->size = srcitem->size; + item->rows = srcitem->rows; + item->maxlength = srcitem->maxlength; +#ifdef MENU_SELECT + opt = curopt = NULL; + for (srcopt = srcitem->select_option; srcopt; srcopt = srcopt->next) { + if (!srcopt->checked) + continue; + opt = New(FormSelectOptionItem); + opt->value = Strdup(srcopt->value); + opt->label = Strdup(srcopt->label); + opt->checked = srcopt->checked; + if (item->select_option == NULL) { + item->select_option = curopt = opt; + } + else { + curopt->next = opt; + curopt = curopt->next; + } + } + item->select_option = opt; + if (srcitem->label) + item->label = Strdup(srcitem->label); +#endif /* MENU_SELECT */ + item->parent = list; + item->next = NULL; + + if (list->lastitem == NULL) { + list->item = list->lastitem = item; + } + else { + list->lastitem->next = item; + list->lastitem = item; + } + + if (srcitem == src) + ret = item; + } + + return ret; +} + +static void +query_from_followform(Str * query, FormItemList * fi, int multipart) +{ + FormItemList *f2; + FILE *body = NULL; + + if (multipart) { + *query = tmpfname(TMPF_DFL, NULL); + body = fopen((*query)->ptr, "w"); + if (body == NULL) { + return; + } + fi->parent->body = (*query)->ptr; + fi->parent->boundary = Sprintf("------------------------------%d%ld%ld%ld", + getpid(), fi->parent, fi->parent->body, fi->parent->boundary)->ptr; + } + *query = Strnew(); + for (f2 = fi->parent->item; f2; f2 = f2->next) { + if (f2->name == NULL) + continue; + /* <ISINDEX> is translated into single text form */ + if (f2->name->length == 0 && + (multipart || f2->type != FORM_INPUT_TEXT)) + continue; + switch (f2->type) { + case FORM_INPUT_RESET: + /* do nothing */ + continue; + case FORM_INPUT_SUBMIT: + case FORM_INPUT_IMAGE: + if (f2 != fi || f2->value == NULL) + continue; + break; + case FORM_INPUT_RADIO: + case FORM_INPUT_CHECKBOX: + if (!f2->checked) + continue; + } + if (multipart) { + if (f2->type == FORM_INPUT_IMAGE) { + *query = Strdup(f2->name); + Strcat_charp(*query, ".x"); + form_write_data(body, fi->parent->boundary, (*query)->ptr, "1"); + *query = Strdup(f2->name); + Strcat_charp(*query, ".y"); + form_write_data(body, fi->parent->boundary, (*query)->ptr, "1"); + } + else if (f2->name && f2->name->length > 0) { + /* not IMAGE */ + if (f2->value != NULL) { +#ifdef JP_CHARSET + if (fi->parent->charset != 0) + *query = conv_str(f2->value, InnerCode, fi->parent->charset); + else + *query = conv_str(f2->value, InnerCode, Currentbuf->document_code); +#else /* not JP_CHARSET */ + *query = f2->value; +#endif /* not JP_CHARSET */ + } + if (f2->type == FORM_INPUT_FILE) + form_write_form_file(body, fi->parent->boundary, f2->name->ptr, (*query)->ptr); + else + form_write_data(body, fi->parent->boundary, f2->name->ptr, (*query)->ptr); + } + } + else { + /* not multipart */ + if (f2->type == FORM_INPUT_IMAGE) { + Strcat(*query, f2->name); + Strcat_charp(*query, ".x=1&"); + Strcat(*query, f2->name); + Strcat_charp(*query, ".y=1"); + } + else { + /* not IMAGE */ + if (f2->name && f2->name->length > 0) { + Strcat(*query, form_quote(f2->name)); + Strcat_char(*query, '='); + } + if (f2->value != NULL) { + if (fi->parent->method == FORM_METHOD_INTERNAL) + Strcat(*query, form_quote(f2->value)); + else { +#ifdef JP_CHARSET + if (fi->parent->charset != 0) + Strcat(*query, + form_quote(conv_str(f2->value, + InnerCode, + fi->parent->charset))); + else + Strcat(*query, + form_quote(conv_str(f2->value, + InnerCode, + Currentbuf->document_code))); +#else /* not JP_CHARSET */ + Strcat(*query, form_quote(f2->value)); +#endif /* not JP_CHARSET */ + } + } + } + if (f2->next) + Strcat_char(*query, '&'); + } + } + if (multipart) { + fprintf(body, "--%s--\r\n", fi->parent->boundary); + fclose(body); + } + else { + /* remove trailing & */ + while (Strlastchar(*query) == '&') + Strshrink(*query, 1); + } +} + +/* process form */ +void +followForm(void) +{ + Line *l; + Anchor *a; + char *p; + FormItemList *fi, *f2; + Str tmp = Strnew(), tmp2 = Strnew(); + int multipart = 0; + + if (Currentbuf->firstLine == NULL) + return; + l = Currentbuf->currentLine; + + a = retrieveCurrentForm(Currentbuf); + if (a == NULL) + return; + fi = (FormItemList *) a->url; + switch (fi->type) { + case FORM_INPUT_TEXT: + p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist); + if (p == NULL) + return; + fi->value = Strnew_charp(p); + formUpdateBuffer(a, Currentbuf, fi); + if (fi->accept || fi->parent->nitems == 1) + goto do_submit; + break; + case FORM_INPUT_FILE: + p = inputFilenameHist("Filename:", fi->value ? fi->value->ptr : NULL, NULL); + if (p == NULL) + return; + fi->value = Strnew_charp(p); + formUpdateBuffer(a, Currentbuf, fi); + if (fi->accept || fi->parent->nitems == 1) + goto do_submit; + break; + case FORM_INPUT_PASSWORD: + p = inputLine("TEXT:", fi->value ? fi->value->ptr : NULL, IN_PASSWORD); + if (p == NULL) + return; + fi->value = Strnew_charp(p); + formUpdateBuffer(a, Currentbuf, fi); + if (fi->accept) + goto do_submit; + break; + case FORM_TEXTAREA: + if (fi->rows == 1) { + p = inputStrHist("TEXT:", fi->value ? fi->value->ptr : NULL, TextHist); + if (p == NULL) + return; + fi->value = Strnew_charp(p); + } + else + input_textarea(fi); + formUpdateBuffer(a, Currentbuf, fi); + break; + case FORM_INPUT_RADIO: + form_recheck_radio(fi, Currentbuf, do_update_form_radio); + break; + case FORM_INPUT_CHECKBOX: + fi->checked = !fi->checked; + formUpdateBuffer(a, Currentbuf, fi); + break; +#ifdef MENU_SELECT + case FORM_SELECT: + formChooseOptionByMenu(fi, + Currentbuf->cursorX - Currentbuf->pos + a->start.pos, + Currentbuf->cursorY); + formUpdateBuffer(a, Currentbuf, fi); + break; +#endif /* MENU_SELECT */ + case FORM_INPUT_IMAGE: + case FORM_INPUT_SUBMIT: + case FORM_INPUT_BUTTON: + do_submit: + multipart = (fi->parent->method == FORM_METHOD_POST && + fi->parent->enctype == FORM_ENCTYPE_MULTIPART); + query_from_followform(&tmp, fi, multipart); + + tmp2 = Strdup(fi->parent->action); + if (!Strcmp_charp(tmp2, "!CURRENT_URL!")) { + /* It means "current URL" */ + tmp2 = parsedURL2Str(&Currentbuf->currentURL); + } + + if (fi->parent->method == FORM_METHOD_GET) { + Strcat_charp(tmp2, "?"); + Strcat(tmp2, tmp); + loadLink(tmp2->ptr, a->target, NULL, NULL); + } + else if (fi->parent->method == FORM_METHOD_POST) { + Buffer *buf; + if (multipart) { + struct stat st; + stat(fi->parent->body, &st); + fi->parent->length = st.st_size; + } + else { + fi->parent->body = tmp->ptr; + fi->parent->length = tmp->length; + } + buf = loadLink(tmp2->ptr, a->target, NULL, fi->parent); + if (multipart) { + unlink(fi->parent->body); + } + if (buf && !(buf->bufferprop & BP_REDIRECTED)) { /* buf must be Currentbuf */ + /* BP_REDIRECTED means that the buffer is obtained through + * Location: header. In this case, buf->form_submit must not be set + * because the page is not loaded by POST method but GET method. + */ + buf->form_submit = save_submit_formlist(fi); + } + } + else if ((fi->parent->method == FORM_METHOD_INTERNAL && + Strcmp_charp(fi->parent->action,"map") == 0) || + Currentbuf->bufferprop & BP_INTERNAL) { /* internal */ + do_internal(tmp2->ptr, tmp->ptr); + return; + } + else { + disp_err_message("Can't send form because of illegal method.", FALSE); + } + break; + case FORM_INPUT_RESET: + for (f2 = fi->parent->item; f2; f2 = f2->next) { + if (f2->name && f2->value && + f2->type != FORM_INPUT_RADIO && + f2->type != FORM_INPUT_CHECKBOX && + f2->type != FORM_INPUT_SUBMIT && + f2->type != FORM_INPUT_HIDDEN && + f2->type != FORM_INPUT_RESET) { + f2->value = Strnew(); + formUpdateBuffer(&Currentbuf->formitem->anchors[f2->anchor_num], Currentbuf, f2); + } + } + break; + case FORM_INPUT_HIDDEN: + default: + break; + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +static void +drawAnchorCursor0(Buffer * buf, int hseq, int prevhseq, int tline, int eline, int active) +{ + int i, j; + Line *l; + Anchor *an; + + l = buf->topLine; + for (j = 0; j < buf->href->nanchor; j++) { + an = &buf->href->anchors[j]; + if (an->start.line < tline) + continue; + if (an->start.line >= eline) + return; + for (;; l = l->next) { + if (l == NULL) + return; + if (l->linenumber == an->start.line) + break; + } + if (hseq >= 0 && an->hseq == hseq) { + for (i = an->start.pos; i < an->end.pos; i++) { + if (l->propBuf[i] & (PE_IMAGE | PE_ANCHOR | PE_FORM)) { + if (active) + l->propBuf[i] |= PE_ACTIVE; + else + l->propBuf[i] &= ~PE_ACTIVE; + } + } + if (active) + redrawLineRegion(buf, l, l->linenumber - tline, + an->start.pos, an->end.pos); + } + else if (prevhseq >= 0 && an->hseq == prevhseq) { + if (active) + redrawLineRegion(buf, l, l->linenumber - tline, + an->start.pos, an->end.pos); + } + } +} + + +void +drawAnchorCursor(Buffer * buf) +{ + Anchor *an; + int hseq, prevhseq; + int tline, eline; + + if (buf->firstLine == NULL) + return; + if (buf->href == NULL) + return; + + an = retrieveCurrentAnchor(buf); + if (an != NULL) + hseq = an->hseq; + else + hseq = -1; + tline = buf->topLine->linenumber; + eline = tline + LASTLINE; + prevhseq = buf->hmarklist->prevhseq; + + drawAnchorCursor0(buf, hseq, prevhseq, tline, eline, 1); + drawAnchorCursor0(buf, hseq, -1, tline, eline, 0); + buf->hmarklist->prevhseq = hseq; +} + +/* underline an anchor if cursor is on the anchor. */ +void +onA(void) +{ + drawAnchorCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* go to the top anchor */ +void +topA(void) +{ + HmarkerList *hl = Currentbuf->hmarklist; + BufferPoint *po; + Anchor *an; + int hseq; + + if (Currentbuf->firstLine == NULL) + return; + if (!hl || hl->nmark == 0) + return; + + hseq = 0; + do { + if (hseq >= hl->nmark) + return; + po = hl->marks + hseq; + an = retrieveAnchor(Currentbuf->href, po->line, po->pos); + if (an == NULL) + an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos); + hseq++; + } while (an == NULL); + + gotoLine(Currentbuf, po->line); + Currentbuf->pos = po->pos; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* go to the last anchor */ +void +lastA(void) +{ + HmarkerList *hl = Currentbuf->hmarklist; + BufferPoint *po; + Anchor *an; + int hseq; + + if (Currentbuf->firstLine == NULL) + return; + if (!hl || hl->nmark == 0) + return; + + hseq = hl->nmark - 1; + do { + if (hseq < 0) + return; + po = hl->marks + hseq; + an = retrieveAnchor(Currentbuf->href, po->line, po->pos); + if (an == NULL) + an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos); + hseq--; + } while (an == NULL); + + gotoLine(Currentbuf, po->line); + Currentbuf->pos = po->pos; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* go to the next anchor */ +void +nextA(void) +{ + HmarkerList *hl = Currentbuf->hmarklist; + BufferPoint *po; + Anchor *an, *pan; + int i, x, y; + + if (Currentbuf->firstLine == NULL) + return; + if (!hl || hl->nmark == 0) + return; + + an = retrieveCurrentAnchor(Currentbuf); + if (an == NULL) + an = retrieveCurrentForm(Currentbuf); + + y = Currentbuf->currentLine->linenumber; + x = Currentbuf->pos; + + for (i = 0; i < PREC_NUM; i++) { + pan = an; + if (an && an->hseq >= 0) { + int hseq = an->hseq + 1; + do { + if (hseq >= hl->nmark) { + pan = an; + goto _end; + } + po = &hl->marks[hseq]; + an = retrieveAnchor(Currentbuf->href, po->line, po->pos); + if (an == NULL) + an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos); + hseq++; + } while (an == NULL || an == pan); + } + else { + an = closest_next_anchor(Currentbuf->href, NULL, x, y); + an = closest_next_anchor(Currentbuf->formitem, an, x, y); + if (an == NULL) { + an = pan; + break; + } + x = an->start.pos; + y = an->start.line; + } + } + + _end: + if (an == NULL || an->hseq < 0) + return; + po = &hl->marks[an->hseq]; + gotoLine(Currentbuf, po->line); + Currentbuf->pos = po->pos; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* go to the previous anchor */ +void +prevA(void) +{ + HmarkerList *hl = Currentbuf->hmarklist; + BufferPoint *po; + Anchor *an, *pan; + int i, x, y; + + if (Currentbuf->firstLine == NULL) + return; + if (!hl || hl->nmark == 0) + return; + + an = retrieveCurrentAnchor(Currentbuf); + if (an == NULL) + an = retrieveCurrentForm(Currentbuf); + + y = Currentbuf->currentLine->linenumber; + x = Currentbuf->pos; + + for (i = 0; i < PREC_NUM; i++) { + pan = an; + if (an && an->hseq >= 0) { + int hseq = an->hseq - 1; + do { + if (hseq < 0) { + an = pan; + goto _end; + } + po = hl->marks + hseq; + an = retrieveAnchor(Currentbuf->href, po->line, po->pos); + if (an == NULL) + an = retrieveAnchor(Currentbuf->formitem, po->line, po->pos); + hseq--; + } while (an == NULL || an == pan); + } + else { + an = closest_prev_anchor(Currentbuf->href, NULL, x, y); + an = closest_prev_anchor(Currentbuf->formitem, an, x, y); + if (an == NULL) { + an = pan; + break; + } + x = an->start.pos; + y = an->start.line; + } + } + + _end: + if (an == NULL || an->hseq < 0) + return; + po = hl->marks + an->hseq; + gotoLine(Currentbuf, po->line); + Currentbuf->pos = po->pos; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_NORMAL); +} + +static int +checkBackBuffer(Buffer * buf) +{ + Buffer *fbuf = buf->linkBuffer[LB_N_FRAME]; + + if (fbuf) { + if (fbuf->frameQ) + return TRUE; /* Currentbuf has stacked frames */ + /* when no frames stacked and next is frame source, try next's + nextBuffer */ + if (RenderFrame && fbuf == buf->nextBuffer) { + if (fbuf->nextBuffer != NULL) + return TRUE; + else + return FALSE; + } + } + + if (buf->nextBuffer) + return TRUE; + + return FALSE; +} + +/* delete current buffer and back to the previous buffer */ +void +backBf(void) +{ + Buffer *buf = Currentbuf->linkBuffer[LB_N_FRAME]; + + if (!checkBackBuffer(Currentbuf)) { + disp_message("Can't back...", TRUE); + return; + } + + delBuffer(Currentbuf); + + if (buf) { + if (buf->frameQ) { + long linenumber; + short pos; + struct frameset *fs; + + fs = popFrameTree(&(buf->frameQ), &linenumber, &pos); + deleteFrameSet(buf->frameset); + buf->frameset = fs; + + if (buf == Currentbuf) { + rFrame(); + gotoLine(Currentbuf, linenumber); + arrangeCursor(Currentbuf); + } + } else if (RenderFrame && buf == Currentbuf) { + delBuffer(Currentbuf); + } + } + + clear(); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +deletePrevBuf() +{ + Buffer *buf = Currentbuf->nextBuffer; + if (buf) + delBuffer(buf); +} + +static void +cmd_loadURL(char *url, ParsedURL * current) +{ + Buffer *buf; + + if (!strncasecmp(url, "mailto:", 7)) { + /* invoke external mailer */ + Str tmp; + char *to = quoteShell(url + 7)->ptr; + if (strcasestr(Mailer, "%s")) + tmp = Sprintf(Mailer, to); + else + tmp = Strnew_m_charp(Mailer, " ", to, NULL); + fmTerm(); + system(tmp->ptr); + fmInit(); + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } +#ifdef USE_NNTP + else if (!strncasecmp(url, "news:", 5) && + strchr(url, '@') == NULL) { + /* news:newsgroup is not supported */ + disp_err_message("news:newsgroup_name is not supported", TRUE); + return; + } +#endif /* USE_NNTP */ + +/* message(Sprintf("loading %s\n", url)->ptr, 0, 0); */ + refresh(); + buf = loadGeneralFile(url, current, NO_REFERER, 0, NULL); + if (buf == NULL) { + char *emsg = Sprintf("Can't load %s\n", url)->ptr; + disp_err_message(emsg, FALSE); + } + else if (buf != NO_BUFFER) { + pushBuffer(buf); + if (RenderFrame && Currentbuf->frameset != NULL) + rFrame(); + } + displayBuffer(Currentbuf, B_NORMAL); +} + + +/* go to specified URL */ +void +goURL(void) +{ + char *url; + ParsedURL p_url; + + url = searchKeyData(); + if (url == NULL) { + if (!(Currentbuf->bufferprop & BP_INTERNAL)) + pushHashHist(URLHist, parsedURL2Str(&Currentbuf->currentURL)->ptr); + url = inputLineHist("Goto URL: ", NULL, IN_URL, URLHist); + if (url != NULL) + SKIP_BLANKS(url); + } + if (url == NULL || *url == '\0') { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + if (*url == '#') { + gotoLabel(url + 1); + return; + } + parseURL2(url, &p_url, NULL); + pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr); + cmd_loadURL(url, baseURL(Currentbuf)); +} + +static void +cmd_loadBuffer(Buffer * buf, int prop, int link) +{ + if (buf == NULL) { + disp_err_message("Can't load string", FALSE); + } + else if (buf != NO_BUFFER) { + buf->bufferprop |= (BP_INTERNAL | prop); + if (!(buf->bufferprop & BP_NO_URL)) + copyParsedURL(&buf->currentURL, &Currentbuf->currentURL); + if (link != LB_NOLINK) { + buf->linkBuffer[REV_LB[link]] = Currentbuf; + Currentbuf->linkBuffer[link] = buf; + } + pushBuffer(buf); + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* load bookmark */ +void +ldBmark(void) +{ + cmd_loadURL(BookmarkFile, NULL); +} + + +/* Add current to bookmark */ +void +adBmark(void) +{ + Str url, title, tmp; + /* cmd_loadBuffer(load_bookmark_panel(Currentbuf), BP_NO_URL, * + * LB_NOLINK); */ + url = form_quote(parsedURL2Str(&Currentbuf->currentURL)); + title = form_quote(Strnew_charp(Currentbuf->buffername)); +#ifdef __EMX__ + tmp = Sprintf("%s/w3mbookmark.exe?mode=panel&bmark=%s&url=%s&title=%s", + get_os2_dft("W3M_LIB_DIR", LIB_DIR), BookmarkFile, url->ptr, title->ptr); +#else /* not __EMX__ */ + tmp = Sprintf("%s/w3mbookmark?mode=panel&bmark=%s&url=%s&title=%s", + LIB_DIR, BookmarkFile, url->ptr, title->ptr); +#endif /* not __EMX__ */ + cmd_loadURL(tmp->ptr, NULL); +} + +/* option setting */ +void +ldOpt(void) +{ + cmd_loadBuffer(load_option_panel(), BP_NO_URL, LB_NOLINK); +} + +/* error message list */ +void +msgs(void) +{ + cmd_loadBuffer(message_list_panel(), BP_NO_URL, LB_NOLINK); +} + +/* page info */ +void +pginfo(void) +{ + Buffer *buf; + + if ((buf = Currentbuf->linkBuffer[LB_N_INFO]) != NULL) { + Currentbuf = buf; + displayBuffer(Currentbuf, B_NORMAL); + return; + } + if ((buf = Currentbuf->linkBuffer[LB_INFO]) != NULL) + delBuffer(buf); + buf = page_info_panel(Currentbuf); +#ifdef JP_CHARSET + if (buf != NULL) + buf->document_code = Currentbuf->document_code; +#endif /* JP_CHARSET */ + cmd_loadBuffer(buf, BP_NORMAL, LB_INFO); +} + +void +follow_map(struct parsed_tagarg *arg) +{ +#ifdef MENU_MAP + Anchor *a; + char *url; + int x; + ParsedURL p_url; + + a = retrieveCurrentImg(Currentbuf); + if (a != NULL) + x = Currentbuf->cursorX - Currentbuf->pos + a->start.pos; + else + x = Currentbuf->cursorX; + url = follow_map_menu(Currentbuf, arg, x, Currentbuf->cursorY + 2); + if (url == NULL || *url == '\0') + return; + if (*url == '#') { + gotoLabel(url + 1); + return; + } + parseURL2(url, &p_url, baseURL(Currentbuf)); + pushHashHist(URLHist, parsedURL2Str(&p_url)->ptr); + cmd_loadURL(url, baseURL(Currentbuf)); +#else + Buffer *buf; + + buf = follow_map_panel(Currentbuf, arg); + if (buf != NULL) + cmd_loadBuffer(buf, BP_NORMAL, LB_NOLINK); +#endif +} + +#ifdef USE_COOKIE +/* cookie list */ +void +cooLst(void) +{ + Buffer *buf; + + buf = cookie_list_panel(); + if (buf != NULL) + cmd_loadBuffer(buf, BP_NO_URL, LB_NOLINK); +} +#endif /* USE_COOKIE */ + +#ifdef USE_HISTORY +/* History page */ +void +ldHist(void) +{ + cmd_loadBuffer(historyBuffer(URLHist), BP_NO_URL, LB_NOLINK); +} +#endif /* USE_HISTORY */ + +/* download HREF link */ +void +svA(void) +{ + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + do_download = TRUE; + followA(); + do_download = FALSE; +} + +/* download IMG link */ +void +svI(void) +{ + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + do_download = TRUE; + followI(); + do_download = FALSE; +} + +/* save buffer */ +void +svBuf(void) +{ + char *file; + FILE *f; + int is_pipe; + + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + file = searchKeyData(); + if (file == NULL || *file == '\0') { + file = inputLineHist("Save buffer to: ", NULL, IN_COMMAND, SaveHist); + if (file == NULL || *file == '\0') { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + } + if (*file == '|') { + is_pipe = TRUE; + f = popen(file + 1, "w"); + } + else { + file = expandName(file); + if (checkOverWrite(file) < 0) + return; + f = fopen(file, "w"); + is_pipe = FALSE; + } + if (f == NULL) { + char *emsg = Sprintf("Can't open %s\n", file)->ptr; + disp_err_message(emsg, TRUE); + return; + } + saveBuffer(Currentbuf, f); + if (is_pipe) + pclose(f); + else + fclose(f); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* save source */ +void +svSrc(void) +{ + if (Currentbuf->sourcefile == NULL) + return; + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + PermitSaveToPipe = TRUE; + doFileCopy(Currentbuf->sourcefile, + guess_save_name(Currentbuf->currentURL.file)); + PermitSaveToPipe = FALSE; + displayBuffer(Currentbuf, B_NORMAL); +} + +static void +_peekURL(int only_img){ + + Anchor *a; + ParsedURL pu; + static Str s = NULL; + static int offset = 0; + + if (Currentbuf->firstLine == NULL) + return; + if (CurrentKey == prev_key && s != NULL) { + if (s->length - offset >= COLS) + offset++; + else if (s->length <= offset) /* bug ? */ + offset = 0; + goto disp; + } else { + offset = 0; + } + a = (only_img ? NULL : retrieveCurrentAnchor(Currentbuf)); + if (a == NULL) { + a = retrieveCurrentImg(Currentbuf); + if (a == NULL) { + a = retrieveCurrentForm(Currentbuf); + if (a == NULL) + return; + s = Strnew_charp(form2str((FormItemList *) a->url)); + goto disp; + } + } + parseURL2(a->url, &pu, baseURL(Currentbuf)); + s = parsedURL2Str(&pu); + disp: + if (PREC_NUM > 1 && s->length > (PREC_NUM - 1) * (COLS - 1)) + disp_message_nomouse(&s->ptr[(PREC_NUM - 1) * (COLS - 1)], TRUE); + else + disp_message_nomouse(&s->ptr[offset], TRUE); +} + +/* peek URL */ +void +peekURL(void) +{ + _peekURL(0); +} + +/* peek URL of image */ +void +peekIMG(void) +{ + _peekURL(1); +} + +/* show current URL */ +static Str +currentURL(void) +{ + if (Currentbuf->bufferprop & BP_INTERNAL) + return Strnew_size(0); + return parsedURL2Str(&Currentbuf->currentURL); +} + +void +curURL(void) +{ + static Str s = NULL; + static int offset = 0; + + if (Currentbuf->bufferprop & BP_INTERNAL) + return; + if (CurrentKey == prev_key && s != NULL) { + if (s->length - offset >= COLS) + offset++; + else if (s->length <= offset) /* bug ? */ + offset = 0; + } else { + offset = 0; + s = currentURL(); + } + if (PREC_NUM > 1 && s->length > (PREC_NUM - 1) * (COLS - 1)) + disp_message_nomouse(&s->ptr[(PREC_NUM - 1) * (COLS - 1)], TRUE); + else + disp_message_nomouse(&s->ptr[offset], TRUE); +} + +/* view HTML source */ +void +vwSrc(void) +{ + char *fn; + Buffer *buf; + + if (Currentbuf->type == NULL || + Currentbuf->bufferprop & BP_FRAME) + return; + if ((buf = Currentbuf->linkBuffer[LB_SOURCE]) != NULL || + (buf = Currentbuf->linkBuffer[LB_N_SOURCE]) != NULL) { + Currentbuf = buf; + displayBuffer(Currentbuf, B_NORMAL); + return; + } + if (Currentbuf->sourcefile == NULL) { + if (Currentbuf->pagerSource && + !strcasecmp(Currentbuf->type, "text/plain")) { + FILE *f; + Str tmpf = tmpfname(TMPF_SRC, NULL); + f = fopen(tmpf->ptr, "w"); + if (f == NULL) + return; + saveBufferDelNum(Currentbuf, f, showLineNum); + fclose(f); + fn = tmpf->ptr; + } + else { + return; + } + } else if (Currentbuf->real_scheme == SCM_LOCAL) { + fn = Currentbuf->filename; + } else { + fn = Currentbuf->sourcefile; + } + if (!strcasecmp(Currentbuf->type, "text/html")) { +#ifdef JP_CHARSET + char old_code = DocumentCode; + DocumentCode = Currentbuf->document_code; +#endif + buf = loadFile(fn); +#ifdef JP_CHARSET + DocumentCode = old_code; +#endif + if (buf == NULL) + return; + buf->type = "text/plain"; + if (Currentbuf->real_type && + !strcasecmp(Currentbuf->real_type, "text/html")) + buf->real_type = "text/plain"; + else + buf->real_type = Currentbuf->real_type; + buf->bufferprop |= BP_SOURCE; + buf->buffername = Sprintf("source of %s", Currentbuf->buffername)->ptr; + buf->linkBuffer[LB_N_SOURCE] = Currentbuf; + Currentbuf->linkBuffer[LB_SOURCE] = buf; + } + else if (!strcasecmp(Currentbuf->type, "text/plain")) { + char *old_type = DefaultType; + DefaultType = "text/html"; + buf = loadGeneralFile(fn, NULL, NO_REFERER, 0, NULL); + DefaultType = old_type; + if (buf == NULL || buf == NO_BUFFER) + return; + if (Currentbuf->real_type && + !strcasecmp(Currentbuf->real_type, "text/plain")) + buf->real_type = "text/html"; + else + buf->real_type = Currentbuf->real_type; + if (!strcmp(buf->buffername, mybasename(fn))) + buf->buffername = Sprintf("HTML view of %s", Currentbuf->buffername)->ptr; + buf->linkBuffer[LB_SOURCE] = Currentbuf; + Currentbuf->linkBuffer[LB_N_SOURCE] = buf; + } + else { + return; + } + buf->currentURL = Currentbuf->currentURL; + buf->real_scheme = Currentbuf->real_scheme; + buf->sourcefile = Currentbuf->sourcefile; + buf->clone = Currentbuf->clone; + (*buf->clone)++; + pushBuffer(buf); + displayBuffer(Currentbuf, B_NORMAL); +} + +/* reload */ +void +reload(void) +{ + Buffer *buf, *fbuf = NULL; + char *type = Currentbuf->type; + Str url; + int top, linenum, cursorY, pos, currentColumn; + FormList *request; + int multipart; + + if (Currentbuf->bufferprop & BP_INTERNAL) { + disp_err_message("Can't reload...", FALSE); + return; + } + if (Currentbuf->currentURL.scheme == SCM_LOCAL && + !strcmp(Currentbuf->currentURL.file, "-")) { + /* file is std input */ + disp_err_message("Can't reload stdin", TRUE); + return; + } + if (Currentbuf->firstLine == NULL) { + top = 1; + linenum = 1; + } else { + top = Currentbuf->topLine->linenumber; + linenum = Currentbuf->currentLine->linenumber; + } + cursorY = Currentbuf->cursorY; + pos = Currentbuf->pos; + currentColumn = Currentbuf->currentColumn; + if (Currentbuf->bufferprop & BP_FRAME && + (fbuf = Currentbuf->linkBuffer[LB_N_FRAME])) { + if (fmInitialized) { + message("Rendering frame", 0, 0); + refresh(); + } + if (!(buf = renderFrame(fbuf, 1))) + return; + if (fbuf->linkBuffer[LB_FRAME]) { + if (buf->sourcefile && + fbuf->linkBuffer[LB_FRAME]->sourcefile && + !strcmp(buf->sourcefile, + fbuf->linkBuffer[LB_FRAME]->sourcefile)) + fbuf->linkBuffer[LB_FRAME]->sourcefile = NULL; + delBuffer(fbuf->linkBuffer[LB_FRAME]); + } + fbuf->linkBuffer[LB_FRAME] = buf; + buf->linkBuffer[LB_N_FRAME] = fbuf; + pushBuffer(buf); + Currentbuf = buf; + if (Currentbuf->firstLine == NULL) { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + gotoLine(Currentbuf, linenum); + Currentbuf->pos = pos; + Currentbuf->currentColumn = currentColumn; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + else if (Currentbuf->frameset != NULL) + fbuf = Currentbuf->linkBuffer[LB_FRAME]; + multipart = 0; + if (Currentbuf->form_submit) { + request = Currentbuf->form_submit->parent; + if (request->method == FORM_METHOD_POST + && request->enctype == FORM_ENCTYPE_MULTIPART) { + Str query; + struct stat st; + multipart = 1; + query_from_followform(&query, Currentbuf->form_submit, multipart); + stat(request->body, &st); + request->length = st.st_size; + } + } + else { + request = NULL; + } + url = parsedURL2Str(&Currentbuf->currentURL); + message("Reloading...", 0, 0); + refresh(); + buf = loadGeneralFile(url->ptr, NULL, NO_REFERER, RG_NOCACHE, request); + if (multipart) + unlink(request->body); + if (buf == NULL) { + disp_err_message("Can't reload...", FALSE); + return; + } + else if (buf == NO_BUFFER) { + return; + } + buf->form_submit = Currentbuf->form_submit; + if (fbuf != NULL) + Firstbuf = deleteBuffer(Firstbuf, fbuf); + repBuffer(Currentbuf, buf); + if ((type != NULL) && (buf->type != NULL) && + ((!strcasecmp(buf->type, "text/plain") && + !strcasecmp(type, "text/html")) || + (!strcasecmp(buf->type, "text/html") && + !strcasecmp(type, "text/plain")))) { + vwSrc(); + if (Currentbuf != buf) + Firstbuf = deleteBuffer(Firstbuf, buf); + } + if (Currentbuf->firstLine == NULL) { + displayBuffer(Currentbuf, B_FORCE_REDRAW); + return; + } + Currentbuf->topLine = lineSkip(Currentbuf, Currentbuf->firstLine, top - 1, FALSE); + gotoLine(Currentbuf, linenum); + Currentbuf->pos = pos; + Currentbuf->currentColumn = currentColumn; + arrangeCursor(Currentbuf); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* mark URL-like patterns as anchors */ +void +chkURL(void) +{ + static char *url_like_pat[] = + { + "http://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$]*", +#ifdef USE_SSL + "https://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./?=~_\\&+@#,\\$]*", +#endif /* USE_SSL */ +#ifdef USE_GOPHER + "gopher://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*", +#endif /* USE_GOPHER */ + "ftp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./=_+@#,\\$]*", +#ifdef USE_NNTP + "news:[^<> ][^<> ]*", + "nntp://[a-zA-Z0-9][a-zA-Z0-9:%\\-\\./_]*", +#endif /* USE_NNTP */ + NULL, + }; + int i; + for (i = 0; url_like_pat[i]; i++) { + reAnchor(Currentbuf, url_like_pat[i]); + } + Currentbuf->check_url |= CHK_URL; + + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +#ifdef USE_NNTP +/* mark Message-ID-like patterns as NEWS anchors */ +void +chkNMID(void) +{ + static char *url_like_pat[] = + { + "<[^<> ][^<> ]*@[A-z0-9\\.\\-_][A-z0-9\\.\\-_]*>", + NULL, + }; + int i; + for (i = 0; url_like_pat[i]; i++) { + reAnchorNews(Currentbuf, url_like_pat[i]); + } + Currentbuf->check_url |= CHK_NMID; + + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} +#endif /* USE_NNTP */ + +/* render frame */ +void +rFrame(void) +{ + Buffer *buf; + + if ((buf = Currentbuf->linkBuffer[LB_FRAME]) != NULL) { + Currentbuf = buf; + displayBuffer(Currentbuf, B_NORMAL); + return; + } + if (Currentbuf->frameset == NULL) { + if ((buf = Currentbuf->linkBuffer[LB_N_FRAME]) != NULL) { + Currentbuf = buf; + displayBuffer(Currentbuf, B_NORMAL); + } + return; + } + if (fmInitialized) { + message("Rendering frame", 0, 0); + refresh(); + } + buf = renderFrame(Currentbuf, 0); + if (buf == NULL) + return; + buf->linkBuffer[LB_N_FRAME] = Currentbuf; + Currentbuf->linkBuffer[LB_FRAME] = buf; + pushBuffer(buf); + if (fmInitialized && display_ok) + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +/* spawn external browser */ +static void +invoke_browser(char *url) +{ + Str tmp; + char *browser = NULL; + int bg = 0; + + CurrentKeyData = NULL; /* not allowed in w3m-control: */ + browser = searchKeyData(); + if (browser == NULL || *browser == '\0') { + switch (prec_num) { + case 0: + case 1: + browser = ExtBrowser; + break; + case 2: + browser = ExtBrowser2; + break; + case 3: + browser = ExtBrowser3; + break; + } + if (browser == NULL || *browser == '\0') { + browser = inputStr("Browse command: ", NULL); + if (browser == NULL || *browser == '\0') + return; + } + } + url = quoteShell(url)->ptr; + if (strcasestr(browser, "%s")) { + tmp = Sprintf(browser, url); + Strremovetrailingspaces(tmp); + if (Strlastchar(tmp) == '&') { + Strshrink(tmp, 1); + bg = 1; + } + } + else { + tmp = Strnew_charp(browser); + Strcat_char(tmp, ' '); + Strcat_charp(tmp, url); + } + fmTerm(); + mySystem(tmp->ptr, bg); + fmInit(); + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +extbrz() +{ + if (Currentbuf->bufferprop & BP_INTERNAL) { + disp_err_message("Can't browse...", FALSE); + return; + } + if (Currentbuf->currentURL.scheme == SCM_LOCAL && + !strcmp(Currentbuf->currentURL.file, "-")) { + /* file is std input */ + disp_err_message("Can't browse stdin", TRUE); + return; + } + invoke_browser(parsedURL2Str(&Currentbuf->currentURL)->ptr); +} + +void +linkbrz() +{ + Anchor *a; + ParsedURL pu; + + if (Currentbuf->firstLine == NULL) + return; + a = retrieveCurrentAnchor(Currentbuf); + if (a == NULL) + return; + parseURL2(a->url, &pu, baseURL(Currentbuf)); + invoke_browser(parsedURL2Str(&pu)->ptr); +} + +/* show current line number and number of lines in the entire document */ +void +curlno() +{ + Str tmp; + int cur = 0, all, col, len = 0; + + if (Currentbuf->currentLine != NULL) { + Line *l = Currentbuf->currentLine; + cur = l->real_linenumber; + if (l->width < 0) + l->width = COLPOS(l, l->len); + len = l->width; + } + col = Currentbuf->currentColumn + Currentbuf->cursorX + 1; + all = (Currentbuf->lastLine ? Currentbuf->lastLine->real_linenumber : Currentbuf->allLine); + if (all == 0 && Currentbuf->lastLine != NULL) + all = Currentbuf->currentLine->real_linenumber; + if (all == 0) + all = 1; + if (Currentbuf->pagerSource && !(Currentbuf->bufferprop & BP_CLOSE)) + tmp = Sprintf("line %d col %d/%d", cur, col, len); + else + tmp = Sprintf("line %d/%d (%d%%) col %d/%d", + cur, all, cur * 100 / all, col, len); +#ifdef JP_CHARSET + Strcat_charp(tmp, " "); + Strcat_charp(tmp, code_to_str(Currentbuf->document_code)); +#endif /* not JP_CHARSET */ + + disp_message(tmp->ptr, FALSE); +} + +#ifdef MOUSE +/* Addition:mouse event */ +#define MOUSE_BTN1_DOWN 0 +#define MOUSE_BTN2_DOWN 1 +#define MOUSE_BTN3_DOWN 2 +#define MOUSE_BTN4_DOWN_RXVT 3 +#define MOUSE_BTN5_DOWN_RXVT 4 +#define MOUSE_BTN4_DOWN_XTERM 64 +#define MOUSE_BTN5_DOWN_XTERM 65 +#define MOUSE_BTN_UP 3 +#define MOUSE_BTN_RESET -1 +#define MOUSE_SCROLL_LINE 5 + +static void +process_mouse(int btn, int x, int y) +{ + int delta_x, delta_y, i; + static int press_btn = MOUSE_BTN_RESET, press_x, press_y; + + if (btn == MOUSE_BTN_UP) { + switch (press_btn) { + case MOUSE_BTN1_DOWN: + if (press_x != x || press_y != y) { + delta_x = x - press_x; + delta_y = y - press_y; + + if (abs(delta_x) < abs(delta_y) / 3) + delta_x = 0; + if (abs(delta_y) < abs(delta_x) / 3) + delta_y = 0; + if (reverse_mouse) { + delta_y = -delta_y; + delta_x = -delta_x; + } + if (delta_y > 0) { + prec_num = delta_y; + ldown1(); + } + else if (delta_y < 0) { + prec_num = -delta_y; + lup1(); + } + if (delta_x > 0) { + prec_num = delta_x; + col1L(); + } + else if (delta_x < 0) { + prec_num = -delta_x; + col1R(); + } + } + else { + if (y == LASTLINE) { + switch (x) { + case 0: + case 1: + backBf(); + break; + case 2: + case 3: + pgBack(); + break; + case 4: + case 5: + pgFore(); + break; + } + return; + } + if (y == Currentbuf->cursorY && + (x == Currentbuf->cursorX +#ifdef JP_CHARSET + || (Currentbuf->currentLine != NULL && + (Currentbuf->currentLine->propBuf[Currentbuf->pos] & PC_KANJI1) + && x == Currentbuf->cursorX + 1) +#endif /* JP_CHARSET */ + )) { + followA(); + return; + } + + cursorXY(Currentbuf, x, y); + displayBuffer(Currentbuf, B_NORMAL); + + } + break; + case MOUSE_BTN2_DOWN: + backBf(); + break; + case MOUSE_BTN3_DOWN: +#ifdef MENU + cursorXY(Currentbuf, x, y); + onA(); + mainMenu(x, y); +#endif /* MENU */ + break; + case MOUSE_BTN4_DOWN_RXVT: + for (i = 0; i < MOUSE_SCROLL_LINE; i++) + ldown1(); + break; + case MOUSE_BTN5_DOWN_RXVT: + for (i = 0; i < MOUSE_SCROLL_LINE; i++) + lup1(); + break; + } + } else if (btn == MOUSE_BTN4_DOWN_XTERM) { + for (i = 0; i < MOUSE_SCROLL_LINE; i++) + ldown1(); + } else if (btn == MOUSE_BTN5_DOWN_XTERM) { + for (i = 0; i < MOUSE_SCROLL_LINE; i++) + lup1(); + } + + if (btn != MOUSE_BTN4_DOWN_RXVT || press_btn == MOUSE_BTN_RESET) { + press_btn = btn; + press_x = x; + press_y = y; + } else { + press_btn = MOUSE_BTN_RESET; + } +} + +void +msToggle(void) +{ + if (use_mouse) { + use_mouse = FALSE; + } + else { + use_mouse = TRUE; + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +mouse() +{ + int btn, x, y; + + btn = (unsigned char) getch() - 32; + x = (unsigned char) getch() - 33; + if (x < 0) + x += 0x100; + y = (unsigned char) getch() - 33; + if (y < 0) + y += 0x100; + + if (x < 0 || x >= COLS || y < 0 || y > LASTLINE) + return; + process_mouse(btn, x, y); +} + +#ifdef USE_GPM +int +gpm_process_mouse(Gpm_Event * event, void *data) +{ + int btn, x, y; + if (event->type & GPM_UP) + btn = MOUSE_BTN_UP; + else if (event->type & GPM_DOWN) { + switch (event->buttons) { + case GPM_B_LEFT: + btn = MOUSE_BTN1_DOWN; + break; + case GPM_B_MIDDLE: + btn = MOUSE_BTN2_DOWN; + break; + case GPM_B_RIGHT: + btn = MOUSE_BTN3_DOWN; + break; + } + } + else { + GPM_DRAWPOINTER(event); + return 0; + } + x = event->x; + y = event->y; + process_mouse(btn, x - 1, y - 1); + return 0; +} +#endif /* USE_GPM */ + +#ifdef USE_SYSMOUSE +int +sysm_process_mouse(int x, int y, int nbs, int obs) +{ + int btn; + int bits; + + if (obs & ~nbs) + btn = MOUSE_BTN_UP; + else if (nbs & ~obs) { + bits = nbs & ~obs; + btn = bits & 0x1 ? MOUSE_BTN1_DOWN : + (bits & 0x2 ? MOUSE_BTN2_DOWN : + (bits & 0x4 ? MOUSE_BTN3_DOWN : 0)); + } + else /* nbs == obs */ + return 0; + process_mouse(btn, x, y); + return 0; +} +#endif /* USE_SYSMOUSE */ +#endif /* MOUSE */ + +void +wrapToggle(void) +{ + if (WrapSearch) { + WrapSearch = FALSE; + disp_message("Wrap search off", FALSE); + } + else { + WrapSearch = TRUE; + disp_message("Wrap search on", FALSE); + } +} + +#ifdef DICT +static char * +GetWord(Buffer * buf) +{ + Line *l = buf->currentLine; + char *lb = l->lineBuf; + int i, b, e, pos = buf->pos; + + i = pos; + while (!IS_ALPHA(lb[i]) && i >= 0) + i--; + pos = i; + while (IS_ALPHA(lb[i]) && i >= 0) + i--; + i++; + if (!IS_ALPHA(lb[i])) + return NULL; + b = i; + i = pos; + while (IS_ALPHA(lb[i]) && i <= l->len - 1) + i++; + e = i - 1; + return Strnew_charp_n(&lb[b], e - b + 1)->ptr; +} + +static void +execdict(char *word) +{ + Buffer *buf; + Str cmd, bn; + MySignalHandler(*prevtrap) (); + + if (word == NULL || *word == '\0') { + displayBuffer(Currentbuf, B_NORMAL); + return; + } + cmd = Strnew_charp(DICTCMD); + Strcat_char(cmd, ' '); + Strcat_charp(cmd, word); + prevtrap = signal(SIGINT, intTrap); + crmode(); + buf = getshell(cmd->ptr); + bn = Sprintf("%s %s", DICTBUFFERNAME, word); + buf->buffername = bn->ptr; + buf->filename = word; + signal(SIGINT, prevtrap); + term_raw(); + if (buf == NULL) { + disp_message("Execution failed", FALSE); + } + else if (buf->firstLine == NULL) { + /* if the dictionary doesn't describe the word. */ + disp_message(Sprintf("Word \"%s\" Not Found", word)->ptr, FALSE); + } + else { + buf->bufferprop |= (BP_INTERNAL | BP_NO_URL); + pushBuffer(buf); + } + displayBuffer(Currentbuf, B_FORCE_REDRAW); +} + +void +dictword(void) +{ + execdict(inputStr("(dictionary)!", "")); +} + +void +dictwordat(void) +{ + execdict(GetWord(Currentbuf)); +} +#endif /* DICT */ + +char * +searchKeyData(void) +{ + KeyListItem *item; + + if (CurrentKeyData != NULL && *CurrentKeyData != '\0') + return allocStr(CurrentKeyData, 0); +#ifdef MENU + if (CurrentMenuData != NULL && *CurrentMenuData != '\0') + return allocStr(CurrentMenuData, 0); +#endif + if (CurrentKey < 0) + return NULL; + item = searchKeyList(&w3mKeyList, CurrentKey); + if (item == NULL || item->data == NULL || *item->data == '\0') + return NULL; + return allocStr(item->data, 0); +} + +#if 0 +static int +searchKeyNum(void) +{ + char *d; + int n = 1; + + d = searchKeyData(); + if (d != NULL) + n = atoi(d); + return n * PREC_NUM; +} +#endif diff --git a/makeallmodel b/makeallmodel new file mode 100755 index 0000000..c6ed23f --- /dev/null +++ b/makeallmodel @@ -0,0 +1,20 @@ +#!/bin/sh +CODE=-code=S +for arg in $* +do + case $arg in + -code=*) + CODE=$arg + ;; + esac +done +for model in baby little mouse cookie +do + for lang in ja en + do + sh configure -yes -model=$model -lang=$lang -cflags=-O $CODE + make clean + make + make bindist + done +done @@ -0,0 +1,222 @@ +/* + * client-side image maps + */ +#include "fm.h" + +#ifdef MENU_MAP +char * +follow_map_menu(Buffer * buf, struct parsed_tagarg *arg, int x, int y) +{ + MapList *ml; + char *name; + TextListItem *t, *s; + int i, n, selected = -1; + char **label; + + name = tag_get_value(arg, "link"); + if (name == NULL) + return NULL; + + for (ml = buf->maplist; ml != NULL; ml = ml->next) { + if (!Strcmp_charp(ml->name, name)) + break; + } + if (ml == NULL) + return NULL; + + for (n = 0, t = ml->urls->first; t != NULL; n++, t = t->next); + if (n == 0) + return NULL; + label = New_N(char *, n + 1); + for (i = 0, t = ml->urls->first, s = ml->alts->first; t != NULL; + i++, t = t->next, s = s->next) + label[i] = *s->ptr ? s->ptr : t->ptr; + label[n] = NULL; + + optionMenu(x, y, label, &selected, 0, NULL); + + if (selected >= 0) { + for (i = 0, t = ml->urls->first; t != NULL; i++, t = t->next) + if (i == selected) + return t->ptr; + } + return NULL; +} + +#else +char *map1 = "<HTML><HEAD><TITLE>Image map links</TITLE></HEAD>\ +<BODY><H1>Image map links</H1>"; + +Buffer * +follow_map_panel(Buffer * buf, struct parsed_tagarg *arg) +{ + Str mappage; + MapList *ml; + char *name; + TextListItem *t, *s; + ParsedURL pu; + + name = tag_get_value(arg, "link"); + if (name == NULL) + return NULL; + + for (ml = buf->maplist; ml != NULL; ml = ml->next) { + if (!Strcmp_charp(ml->name, name)) + break; + } + if (ml == NULL) + return NULL; + + mappage = Strnew_charp(map1); + for (t = ml->urls->first, s = ml->alts->first; t != NULL; + t = t->next, s = s->next) { + parseURL2(t->ptr, &pu, baseURL(buf)); + Strcat_charp(mappage, "<a href=\""); + Strcat_charp(mappage, htmlquote_str(parsedURL2Str(&pu)->ptr)); + Strcat_charp(mappage, "\">"); + Strcat_charp(mappage, htmlquote_str(s->ptr)); + Strcat_charp(mappage, " "); + Strcat_charp(mappage, htmlquote_str(t->ptr)); + Strcat_charp(mappage, "</a><br>\n"); + } + Strcat_charp(mappage, "</body></html>"); + + return loadHTMLString(mappage); +} +#endif + +/* append frame URL */ +static void +append_frame_info(Str html, struct frameset *set, int level) +{ + char *q; + int i, j; + + if (!set) + return; + + for (i = 0; i < set->col * set->row; i++) { + union frameset_element frame = set->frame[i]; + if (frame.element != NULL) { + switch (frame.element->attr) { + case F_UNLOADED: + case F_BODY: + if (frame.body->url == NULL) + break; + for (j = 0; j < level; j++) + Strcat_charp(html, " "); + q = htmlquote_str(frame.body->url); + Strcat_charp(html, "<a href=\""); + Strcat_charp(html, q); + Strcat_charp(html, "\">"); + Strcat_charp(html, htmlquote_str(frame.body->name)); + Strcat_charp(html, " "); + Strcat_charp(html, q); + Strcat_charp(html, "</a>\n"); + break; + case F_FRAMESET: + append_frame_info(html, frame.set, level + 1); + break; + } + } + } +} + +/* + * information of current page and link + */ +Buffer * +page_info_panel(Buffer * buf) +{ + Str tmp = Strnew_size(1024); + Anchor *a; + Str s; + ParsedURL pu; + TextListItem *ti; + struct frameset *f_set = NULL; + int all; + + Strcat_charp(tmp, "<html><head><title>Information about current page</title></head><body>"); + Strcat_charp(tmp, "<h1>Information about current page</h1><table cellpadding=0>"); + if (buf == NULL) + goto end; + Strcat_charp(tmp, "<tr><td nowrap>Title<td>"); + Strcat_charp(tmp, htmlquote_str(buf->buffername)); + Strcat_charp(tmp, "<tr><td nowrap>Current URL<td>"); + Strcat_charp(tmp, htmlquote_str(parsedURL2Str(&buf->currentURL)->ptr)); + Strcat_charp(tmp, "<tr><td nowrap>Document Type<td>"); + if (buf->real_type) + Strcat_charp(tmp, buf->real_type); + else + Strcat_charp(tmp, "unknown"); + Strcat_charp(tmp, "<tr><td nowrap>Last Modified<td>"); + Strcat_charp(tmp, htmlquote_str(last_modified(buf))); +#ifdef JP_CHARSET + Strcat_charp(tmp, "<tr><td nowrap>Document Code<td>"); + Strcat_charp(tmp, code_to_str(buf->document_code)); +#endif /* JP_CHARSET */ + Strcat_charp(tmp, "<tr><td nowrap>Number of line<td>"); + all = buf->allLine; + if (all == 0 && buf->lastLine) + all = buf->lastLine->linenumber; + Strcat(tmp, Sprintf("%d", all)); + Strcat_charp(tmp, "<tr><td nowrap>Transferred byte<td>"); + Strcat(tmp, Sprintf("%d", buf->trbyte)); + + a = retrieveCurrentAnchor(buf); + if (a != NULL) { + parseURL2(a->url, &pu, baseURL(buf)); + s = parsedURL2Str(&pu); + Strcat_charp(tmp, "<tr><td nowrap>URL of current anchor<td>"); + Strcat_charp(tmp, htmlquote_str(s->ptr)); + } + a = retrieveCurrentImg(buf); + if (a != NULL) { + parseURL2(a->url, &pu, baseURL(buf)); + s = parsedURL2Str(&pu); + Strcat_charp(tmp, "<tr><td nowrap>URL of current image<td>"); + Strcat_charp(tmp, "<a href=\""); + Strcat_charp(tmp, htmlquote_str(s->ptr)); + Strcat_charp(tmp, "\">"); + Strcat_charp(tmp, htmlquote_str(s->ptr)); + Strcat_charp(tmp, "</a>"); + } + a = retrieveCurrentForm(buf); + if (a != NULL) { + s = Strnew_charp(form2str((FormItemList *) a->url)); + Strcat_charp(tmp, "<tr><td nowrap>Method/type of current form<td>"); + Strcat_charp(tmp, htmlquote_str(s->ptr)); + } + Strcat_charp(tmp, "</table>\n"); + if (buf->document_header != NULL) { + Strcat_charp(tmp, "<hr width=50%>\n"); + Strcat_charp(tmp, "<h1>Header information</h1>\n"); + for (ti = buf->document_header->first; ti != NULL; ti = ti->next) { + Strcat_charp(tmp, htmlquote_str(ti->ptr)); + Strcat_charp(tmp, "<br>"); + } + } + if (buf->frameset != NULL) + f_set = buf->frameset; + else if (buf->bufferprop & BP_FRAME && + buf->nextBuffer != NULL && + buf->nextBuffer->frameset != NULL) + f_set = buf->nextBuffer->frameset; + + if (f_set) { + Strcat_charp(tmp, "<hr width=50%><h1>Frame information</h1><pre>"); + append_frame_info(tmp, f_set, 0); + Strcat_charp(tmp, "</pre>"); + } +#ifdef USE_SSL + if (buf->ssl_certificate == NULL) + goto end; + Strcat_charp(tmp, "<h1>SSL certificate</h1>\n"); + Strcat_charp(tmp, "<pre>\n"); + Strcat_charp(tmp, buf->ssl_certificate); + Strcat_charp(tmp, "</pre>\n"); +#endif + end: + Strcat_charp(tmp, "</body></html>"); + return loadHTMLString(tmp); +} diff --git a/matrix.c b/matrix.c new file mode 100644 index 0000000..8208611 --- /dev/null +++ b/matrix.c @@ -0,0 +1,276 @@ + +/* + * matrix.h, matrix.c: Liner equation solver using LU decomposition. + * $Id: matrix.c,v 1.1 2001/11/08 05:15:20 a-ito Exp $ + * + * by K.Okabe Aug. 1999 + * + * LUfactor, LUsolve, Usolve and Lsolve, are based on the functions in + * Meschach Library Version 1.2b. + */ + +/************************************************************************** +** +** Copyright (C) 1993 David E. Steward & Zbigniew Leyk, all rights reserved. +** +** Meschach Library +** +** This Meschach Library is provided "as is" without any express +** or implied warranty of any kind with respect to this software. +** In particular the authors shall not be liable for any direct, +** indirect, special, incidental or consequential damages arising +** in any way from use of the software. +** +** Everyone is granted permission to copy, modify and redistribute this +** Meschach Library, provided: +** 1. All copies contain this copyright notice. +** 2. All modified copies shall carry a notice stating who +** made the last modification and the date of such modification. +** 3. No charge is made for this software or works derived from it. +** This clause shall not be construed as constraining other software +** distributed on the same medium as this software, nor is a +** distribution fee considered a charge. +** +***************************************************************************/ + +#include "config.h" +#include "matrix.h" +#include "gc.h" + +/* + * Macros from "fm.h". + */ + +#define New(type) ((type*)GC_MALLOC(sizeof(type))) +#define NewAtom(type) ((type*)GC_MALLOC_ATOMIC(sizeof(type))) +#define New_N(type,n) ((type*)GC_MALLOC((n)*sizeof(type))) +#define NewAtom_N(type,n) ((type*)GC_MALLOC_ATOMIC((n)*sizeof(type))) +#define Renew_N(type,ptr,n) ((type*)GC_REALLOC((ptr),(n)*sizeof(type))) + +#define SWAPD(a,b) { double tmp = a; a = b; b = tmp; } +#define SWAPI(a,b) { int tmp = a; a = b; b = tmp; } + +#ifndef NO_FLOAT_H +#include <float.h> +#endif /* not NO_FLOAT_H */ +#if defined(DBL_MAX) +static double Tiny = 10.0 / DBL_MAX; +#elif defined(FLT_MAX) +static double Tiny = 10.0 / FLT_MAX; +#else /* not defined(FLT_MAX) */ +static double Tiny = 1.0e-30; +#endif /* not defined(FLT_MAX */ + +/* + * LUfactor -- gaussian elimination with scaled partial pivoting + * -- Note: returns LU matrix which is A. + */ + +int +LUfactor(Matrix A, int *index) +{ + int dim = A->dim, i, j, k, i_max, k_max; + Vector scale; + double mx, tmp; + + scale = new_vector(dim); + + for (i = 0; i < dim; i++) + index[i] = i; + + for (i = 0; i < dim; i++) { + mx = 0.; + for (j = 0; j < dim; j++) { + tmp = fabs(M_VAL(A, i, j)); + if (mx < tmp) + mx = tmp; + } + scale->ve[i] = mx; + } + + k_max = dim - 1; + for (k = 0; k < k_max; k++) { + mx = 0.; + i_max = -1; + for (i = k; i < dim; i++) { + if (fabs(scale->ve[i]) >= Tiny * fabs(M_VAL(A, i, k))) { + tmp = fabs(M_VAL(A, i, k)) / scale->ve[i]; + if (mx < tmp) { + mx = tmp; + i_max = i; + } + } + } + if (i_max == -1) { + M_VAL(A, k, k) = 0.; + continue; + } + + if (i_max != k) { + SWAPI(index[i_max], index[k]); + for (j = 0; j < dim; j++) + SWAPD(M_VAL(A, i_max, j), M_VAL(A, k, j)); + } + + for (i = k + 1; i < dim; i++) { + tmp = M_VAL(A, i, k) = M_VAL(A, i, k) / M_VAL(A, k, k); + for (j = k + 1; j < dim; j++) + M_VAL(A, i, j) -= tmp * M_VAL(A, k, j); + } + } + return 0; +} + +/* + * LUsolve -- given an LU factorisation in A, solve Ax=b. + */ + +int +LUsolve(Matrix A, int *index, Vector b, Vector x) +{ + int i, dim = A->dim; + + for (i = 0; i < dim; i++) + x->ve[i] = b->ve[index[i]]; + + if (Lsolve(A, x, x, 1.) == -1 || Usolve(A, x, x, 0.) == -1) + return -1; + return 0; +} + +/* m_inverse -- returns inverse of A, provided A is not too rank deficient + * * * * * * * -- uses LU factorisation */ +#if 0 +Matrix +m_inverse(Matrix A, Matrix out) +{ + int *index = NewAtom_N(int, A->dim); + Matrix A1 = new_matrix(A->dim); + m_copy(A, A1); + LUfactor(A1, index); + return LUinverse(A1, index, out); +} +#endif /* 0 */ + +Matrix +LUinverse(Matrix A, int *index, Matrix out) +{ + int i, j, dim = A->dim; + Vector tmp, tmp2; + + if (!out) + out = new_matrix(dim); + tmp = new_vector(dim); + tmp2 = new_vector(dim); + for (i = 0; i < dim; i++) { + for (j = 0; j < dim; j++) + tmp->ve[j] = 0.; + tmp->ve[i] = 1.; + if (LUsolve(A, index, tmp, tmp2) == -1) + return NULL; + for (j = 0; j < dim; j++) + M_VAL(out, j, i) = tmp2->ve[j]; + } + return out; +} + +/* + * Usolve -- back substitution with optional over-riding diagonal + * -- can be in-situ but doesn't need to be. + */ + +int +Usolve(Matrix mat, Vector b, Vector out, double diag) +{ + int i, j, i_lim, dim = mat->dim; + double sum; + + for (i = dim - 1; i >= 0; i--) { + if (b->ve[i] != 0.) + break; + else + out->ve[i] = 0.; + } + i_lim = i; + + for (; i >= 0; i--) { + sum = b->ve[i]; + for (j = i + 1; j <= i_lim; j++) + sum -= M_VAL(mat, i, j) * out->ve[j]; + if (diag == 0.) { + if (fabs(M_VAL(mat, i, i)) <= Tiny * fabs(sum)) + return -1; + else + out->ve[i] = sum / M_VAL(mat, i, i); + } + else + out->ve[i] = sum / diag; + } + + return 0; +} + +/* + * Lsolve -- forward elimination with (optional) default diagonal value. + */ + +int +Lsolve(Matrix mat, Vector b, Vector out, double diag) +{ + int i, j, i_lim, dim = mat->dim; + double sum; + + for (i = 0; i < dim; i++) { + if (b->ve[i] != 0.) + break; + else + out->ve[i] = 0.; + } + i_lim = i; + + for (; i < dim; i++) { + sum = b->ve[i]; + for (j = i_lim; j < i; j++) + sum -= M_VAL(mat, i, j) * out->ve[j]; + if (diag == 0.) { + if (fabs(M_VAL(mat, i, i)) <= Tiny * fabs(sum)) + return -1; + else + out->ve[i] = sum / M_VAL(mat, i, i); + } + else + out->ve[i] = sum / diag; + } + + return 0; +} + +/* + * new_matrix -- generate a nxn matrix. + */ + +Matrix +new_matrix(int n) +{ + Matrix mat; + + mat = New(struct matrix); + mat->dim = n; + mat->me = NewAtom_N(double, n * n); + return mat; +} + +/* + * new_matrix -- generate a n-dimension vector. + */ + +Vector +new_vector(int n) +{ + Vector vec; + + vec = New(struct vector); + vec->dim = n; + vec->ve = NewAtom_N(double, n); + return vec; +} diff --git a/matrix.h b/matrix.h new file mode 100644 index 0000000..254a2e6 --- /dev/null +++ b/matrix.h @@ -0,0 +1,73 @@ +/* + * matrix.h, matrix.c: Liner equation solver using LU decomposition. + * $Id: matrix.h,v 1.1 2001/11/08 05:15:21 a-ito Exp $ + * + * by K.Okabe Aug. 1999 + * + * You can use,copy,modify and distribute this program without any permission. + */ + +#ifndef _MATRIX_H +#include <math.h> +#include <string.h> + +/* + * Types. + */ + +struct matrix { + double *me; + int dim; +}; + +struct vector { + double *ve; + int dim; +}; + +typedef struct matrix *Matrix; +typedef struct vector *Vector; + +/* + * Macros. + */ + +#define M_VAL(m,i,j) ((m)->me[(i)*(m)->dim +(j)]) +#define V_VAL(v,i) ((v)->ve[i]) + +/* + * Compatible macros with those in Meschach Library. + */ + +#define m_entry(m,i,j) (M_VAL(m,i,j)) +#define v_entry(v,i) (V_VAL(v,i)) +#ifdef __CYGWIN__ +#define m_copy(m1,m2) (bcopy((const char *)(m1)->me,(char *)(m2)->me,(m1)->dim*(m1)->dim*sizeof(double))) +#else /* not __CYGWIN__ */ +#define m_copy(m1,m2) (bcopy((m1)->me,(m2)->me,(m1)->dim*(m1)->dim*sizeof(double))) +#endif /* not __CYGWIN__ */ +#define v_free(v) ((v)=NULL) +#define m_free(m) ((m)=NULL) +#define px_free(px) ((px)=NULL) +#define m_set_val(m,i,j,x) (M_VAL(m,i,j) = (x)) +#define m_add_val(m,i,j,x) (M_VAL(m,i,j) += (x)) +#define v_set_val(v,i,x) (V_VAL(v,i) = (x)) +#define v_add_val(v,i,x) (V_VAL(v,i) += (x)) +#define m_get(r,c) (new_matrix(r)) +#define v_get(n) (new_vector(n)) +#define px_get(n) (New_N(int,n)) +typedef struct matrix MAT; +typedef struct vector VEC; +typedef int PERM; + +extern int LUfactor(Matrix, int *); +extern Matrix m_inverse(Matrix, Matrix); +extern Matrix LUinverse(Matrix, int *, Matrix); +extern int LUsolve(Matrix, int *, Vector, Vector); +extern int Lsolve(Matrix, Vector, Vector, double); +extern int Usolve(Matrix, Vector, Vector, double); +extern Matrix new_matrix(int); +extern Vector new_vector(int); + +#define _MATRIX_H +#endif /* _MATRIX_H */ @@ -0,0 +1,1547 @@ + +/* + * w3m menu.c + */ +#include <stdio.h> + +#include "fm.h" +#include "menu.h" +#include "func.h" +#include "myctype.h" +#include "regex.h" + +#ifdef MOUSE +#ifdef USE_GPM +#include <gpm.h> +static int gpm_process_menu_mouse(Gpm_Event * event, void *data); +extern int gpm_process_mouse(Gpm_Event *, void *); +#endif /* USE_GPM */ +#ifdef USE_SYSMOUSE +extern int (*sysm_handler) (int x, int y, int nbs, int obs); +static int sysm_process_menu_mouse(int, int, int, int); +extern int sysm_process_mouse(int, int, int, int); +#endif /* USE_SYSMOUSE */ +#if defined(USE_GPM) || defined(USE_SYSMOUSE) +#define X_MOUSE_SELECTED (char)0xff +static int X_Mouse_Selection; +extern int do_getch(); +#define getch() do_getch() +#endif /* defined(USE_GPM) || * * * * * * + * defined(USE_SYSMOUSE) */ +#endif /* MOUSE */ + +#ifdef MENU + +#ifdef KANJI_SYMBOLS +static char *FRAME[] = +{ +#ifdef MENU_THIN_FRAME + "��", "��", "��", + "��", " ", "��", + "��", "��", "��", +#else /* not MENU_THIN_FRAME */ + "��", "��", "��", + "��", " ", "��", + "��", "��", "��", +#endif /* not MENU_THIN_FRAME */ + "��", "��"}; +#define FRAME_WIDTH 2 + +#define G_start +/**/ +#define G_end /**/ + +#else /* not KANJI_SYMBOLS */ +static char *N_FRAME[] = +{ + "+", "-", "+", + "|", " ", "|", + "+", "-", "+", + ":", ":"}; +#define FRAME_WIDTH 1 + +static char *G_FRAME[] = +{ + "l", "q", "k", + "x", " ", "x", + "m", "q", "j", + ":", ":"}; + +static char **FRAME = NULL; +static int graph_mode = FALSE; + +#define G_start {if (graph_mode) graphstart();} +#define G_end {if (graph_mode) graphend();} +#endif /* not KANJI_SYMBOLS */ + +static int mEsc(char c); +static int mEscB(char c); +static int mEscD(char c); +static int mNull(char c); +static int mSelect(char c); +static int mDown(char c); +static int mUp(char c); +static int mLast(char c); +static int mTop(char c); +static int mNext(char c); +static int mPrev(char c); +static int mFore(char c); +static int mBack(char c); +static int mOk(char c); +static int mCancel(char c); +static int mClose(char c); +static int mSusp(char c); +static int mMouse(char c); +static int mSrchF(char c); +static int mSrchB(char c); +static int mSrchN(char c); +static int mSrchP(char c); +#ifdef __EMX__ +static int mPc(char c); +#endif + +static int (*MenuKeymap[128]) (char c) = { +/* C-@ C-a C-b C-c C-d C-e C-f C-g */ +#ifdef __EMX__ + mPc, mTop, mPrev, mClose, mNull, mLast, mNext, mNull, +#else + mNull, mTop, mPrev, mClose, mNull, mLast, mNext, mNull, +#endif +/* C-h C-i C-j C-k C-l C-m C-n C-o */ + mCancel,mNull, mOk, mNull, mNull, mOk, mDown, mNull, +/* C-p C-q C-r C-s C-t C-u C-v C-w */ + mUp, mNull, mNull, mNull, mNull, mNull, mNext, mNull, +/* C-x C-y C-z C-[ C-\ C-] C-^ C-_ */ + mNull, mNull, mSusp, mEsc, mNull, mNull, mNull, mNull, +/* SPC ! " # $ % & ' */ + mOk, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* ( ) * + , - . / */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mSrchF, +/* 0 1 2 3 4 5 6 7 */ + mNull, mNull, mNull, mNull, mNull, mNull , mNull, mNull, +/* 8 9 : ; < = > ? */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mSrchB, +/* @ A B C D E F G */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* H I J K L M N O */ + mNull, mNull, mNull, mNull, mNull, mNull, mSrchP, mNull, +/* P Q R S T U V W */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* X Y Z [ \ ] ^ _ */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* ` a b c d e f g */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* h i j k l m n o */ + mCancel,mNull, mDown, mUp, mOk, mNull, mSrchN, mNull, +/* p q r s t u v w */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* x y z { | } ~ DEL */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mCancel, +}; +static int (*MenuEscKeymap[128]) (char c) = { + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* O */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mEscB, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* [ */ + mNull, mNull, mNull, mEscB, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* v */ + mNull, mNull, mNull, mNull, mNull, mNull, mPrev, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +}; +static int (*MenuEscBKeymap[128]) (char c) = { + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* A B C D E */ + mNull, mUp, mDown, mOk, mCancel,mClose, mNull, mNull, +/* L M */ + mNull, mNull, mNull, mNull, mClose, mMouse, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +}; +static int (*MenuEscDKeymap[128]) (char c) = { +/* 0 1 INS 3 4 PgUp, PgDn 7 */ + mNull, mNull, mClose, mNull, mNull, mBack, mFore, mNull, +/* 8 9 10 F1 F2 F3 F4 F5 */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* 16 F6 F7 F8 F9 F10 22 23 */ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +/* 24 25 26 27 HELP 29 30 31 */ + mNull, mNull, mNull, mNull, mClose, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +}; + +#ifdef __EMX__ +static int (*MenuPcKeymap[256])(char c)={ +// Null + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// S-Tab + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-q A-w A-E A-r A-t A-y A-u A-i + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-o A-p A-[ A-] A-a A-s + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-d A-f A-g A-h A-j A-k A-l A-; + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-' A-' A-\ A-x A-c A-v + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mPrev, +// A-b A-n A-m A-, A-. A-/ A-+ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// F1 F2 F3 F4 F5 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// F6 F7 F8 F9 F10 Home + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mTop, +// Up PgUp A-/ Left 5 Right C-* End + mUp, mUp, mNull, mCancel,mNull, mOk, mNull, mLast, +// Down PgDn Ins Del S-F1 S-F2 S-F3 S-F4 + mDown, mDown, mClose, mCancel,mNull, mNull, mNull, mNull, +// S-F5 S-F6 S-F7 S-F8 S-F9 S-F10 C-F1 C-F2 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// C-F3 C-F4 C-F5 C-F6 C-F7 C-F8 C-F9 C-F10 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-F1 A-F2 A-F3 A-F4 A-F5 A-F6 A-F7 A-F8 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-F9 A-F10 PrtSc C-Left C-Right C-End C-PgDn C-Home + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-1 A-2 A-3 A-4 A-5 A-6 A-7/8 A-9 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A-0 A - A-= C-PgUp F11 F12 S-F11 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// S-F12 C-F11 C-F12 A-F11 A-F12 C-Up C-/ C-5 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// S-* C-Down C-Ins C-Del C-Tab C - C-+ + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, +// A - A-Tab A-Enter + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 160 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 168 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 176 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 184 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 192 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 200 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 208 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 216 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 224 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 232 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull, // 240 + mNull, mNull, mNull, mNull, mNull, mNull, mNull, mNull // 248 +}; +#endif + +/* --- SelectMenu --- */ + +static Menu SelectMenu; +static int SelectV = 0; +static void initSelectMenu(void); +static void smChBuf(void); +static int smDelBuf(char c); + +/* --- SelectMenu (END) --- */ + +/* --- MainMenu --- */ + +static Menu MainMenu; +#if LANG == JA +static MenuItem MainMenuItem[] = +{ +/* type label variabel value func popup keys data */ + {MENU_FUNC, "��� (b)", NULL, 0, backBf, NULL, "b", NULL}, + {MENU_FUNC, "�Хåե����� (s)", NULL, 0, selBuf, NULL, "s", NULL}, + {MENU_FUNC, "��������ɽ�� (v)", NULL, 0, vwSrc, NULL, "vV", NULL}, + {MENU_FUNC, "���������Խ� (e)", NULL, 0, editBf, NULL, "eE", NULL}, + {MENU_FUNC, "����������¸ (S)", NULL, 0, svSrc, NULL, "S", NULL}, + {MENU_FUNC, "���ɤ߹��� (r)", NULL, 0, reload, NULL, "rR", NULL}, + {MENU_NOP, "����������������", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, "���ɽ�� (a)", NULL, 0, followA, NULL, "a", NULL}, + {MENU_FUNC, "�����¸ (A)", NULL, 0, svA, NULL, "A", NULL}, + {MENU_FUNC, "������ɽ�� (i)", NULL, 0, followI, NULL, "i", NULL}, + {MENU_FUNC, "��������¸ (I)", NULL, 0, svI, NULL, "I", NULL}, + {MENU_FUNC, "�ե졼��ɽ�� (f)", NULL, 0, rFrame, NULL, "fF", NULL}, + {MENU_NOP, "����������������", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, "�֥å��ޡ��� (B)", NULL, 0, ldBmark, NULL, "B", NULL}, + {MENU_FUNC, "�إ�� (h)", NULL, 0, ldhelp, NULL, "hH", NULL}, + {MENU_FUNC, "���ץ���� (o)", NULL, 0, ldOpt, NULL, "oO", NULL}, + {MENU_NOP, "����������������", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, "��λ (q)", NULL, 0, qquitfm, NULL, "qQ", NULL}, + {MENU_END, "", NULL, 0, nulcmd, NULL, "", NULL}, +}; +#else /* LANG != JA */ +static MenuItem MainMenuItem[] = +{ +/* type label variable value func popup keys data */ + {MENU_FUNC, " Back (b) ", NULL, 0, backBf, NULL, "b", NULL}, + {MENU_FUNC, " Select Buffer(s) ", NULL, 0, selBuf, NULL, "s", NULL}, + {MENU_FUNC, " View Source (v) ", NULL, 0, vwSrc, NULL, "vV", NULL}, + {MENU_FUNC, " Edit Source (e) ", NULL, 0, editBf, NULL, "eE", NULL}, + {MENU_FUNC, " Save Source (S) ", NULL, 0, svSrc, NULL, "S", NULL}, + {MENU_FUNC, " Reload (r) ", NULL, 0, reload, NULL, "rR", NULL}, + {MENU_NOP, " ---------------- ", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, " Go Link (a) ", NULL, 0, followA, NULL, "a", NULL}, + {MENU_FUNC, " Save Link (A) ", NULL, 0, svA, NULL, "A", NULL}, + {MENU_FUNC, " View Image (i) ", NULL, 0, followI, NULL, "i", NULL}, + {MENU_FUNC, " Save Image (I) ", NULL, 0, svI, NULL, "I", NULL}, + {MENU_FUNC, " View Frame (f) ", NULL, 0, rFrame, NULL, "fF", NULL}, + {MENU_NOP, " ---------------- ", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, " Bookmark (B) ", NULL, 0, ldBmark, NULL, "B", NULL}, + {MENU_FUNC, " Help (h) ", NULL, 0, ldhelp, NULL, "hH", NULL}, + {MENU_FUNC, " Option (o) ", NULL, 0, ldOpt, NULL, "oO", NULL}, + {MENU_NOP, " ---------------- ", NULL, 0, nulcmd, NULL, "", NULL}, + {MENU_FUNC, " Quit (q) ", NULL, 0, qquitfm, NULL, "qQ", NULL}, + {MENU_END, "", NULL, 0, nulcmd, NULL, "", NULL}, +}; +#endif /* LANG != JA */ + +/* --- MainMenu (END) --- */ + +extern int w3mNFuncList; +extern FuncList w3mFuncList[]; +static MenuList *w3mMenuList; + +static Menu *CurrentMenu = NULL; + +#define mvaddch(y, x, c) (move(y, x), addch(c)) +#define mvaddstr(y, x, str) (move(y, x), addstr(str)) +#define mvaddnstr(y, x, str, n) (move(y, x), addnstr_sup(str, n)) + +void +new_menu(Menu * menu, MenuItem * item) +{ + int i, l; + char *p; + + menu->cursorX = 0; + menu->cursorY = 0; + menu->x = 0; + menu->y = 0; + menu->nitem = 0; + menu->item = item; + menu->initial = 0; + menu->select = 0; + menu->offset = 0; + menu->active = 0; + + if (item == NULL) + return; + + for (i = 0; item[i].type != MENU_END; i++); + menu->nitem = i; + menu->height = menu->nitem; + for (i = 0; i < 128; i++) + menu->keymap[i] = MenuKeymap[i]; + menu->width = 0; + for (i = 0; i < menu->nitem; i++) { + if ((p = item[i].keys) != NULL) { + while (*p) { + if (IS_ASCII(*p)) { + menu->keymap[(int) *p] = mSelect; + menu->keyselect[(int) *p] = i; + } + p++; + } + } + l = strlen(item[i].label); + if (l > menu->width) + menu->width = l; + } +} + +void +geom_menu(Menu * menu, int x, int y, int select) +{ + int win_x, win_y, win_w, win_h; + + menu->select = select; + + if (menu->width % FRAME_WIDTH) + menu->width = (menu->width / FRAME_WIDTH + 1) * FRAME_WIDTH; + win_x = menu->x - FRAME_WIDTH; + win_w = menu->width + 2 * FRAME_WIDTH; + if (win_x + win_w > COLS) + win_x = COLS - win_w; + if (win_x < 0) { + win_x = 0; + if (win_w > COLS) { + menu->width = COLS - 2 * FRAME_WIDTH; + menu->width -= menu->width % FRAME_WIDTH; + win_w = menu->width + 2 * FRAME_WIDTH; + } + } + menu->x = win_x + FRAME_WIDTH; + + win_y = menu->y - select - 1; + win_h = menu->height + 2; + if (win_y + win_h > LASTLINE) + win_y = LASTLINE - win_h; + if (win_y < 0) { + win_y = 0; + if (win_y + win_h > LASTLINE) { + win_h = LASTLINE - win_y; + menu->height = win_h - 2; + if (menu->height <= select) + menu->offset = select - menu->height + 1; + } + } + menu->y = win_y + 1; +} + +void +draw_all_menu(Menu * menu) +{ + if (menu->parent != NULL) + draw_all_menu(menu->parent); + draw_menu(menu); +} + +void +draw_menu(Menu * menu) +{ + int x, y, w; + int i, j; + + x = menu->x - FRAME_WIDTH; + w = menu->width + 2 * FRAME_WIDTH; + y = menu->y - 1; + +#ifndef KANJI_SYMBOLS + if (FRAME == NULL) { + if (graph_ok()) { + graph_mode = TRUE; + FRAME = G_FRAME; + } + else { + FRAME = N_FRAME; + } + } +#endif /* not KANJI_SYMBOLS */ + + if (menu->offset == 0) { + G_start; + mvaddstr(y, x, FRAME[0]); + for (i = FRAME_WIDTH; i < w - FRAME_WIDTH; i += FRAME_WIDTH) + mvaddstr(y, x + i, FRAME[1]); + mvaddstr(y, x + i, FRAME[2]); + G_end; + } + else { + G_start; + mvaddstr(y, x, FRAME[3]); + G_end; + for (i = FRAME_WIDTH; i < w - FRAME_WIDTH; i += FRAME_WIDTH) + mvaddstr(y, x + i, FRAME[4]); + G_start; + mvaddstr(y, x + i, FRAME[5]); + G_end; + i = (w / 2 - 1) / FRAME_WIDTH * FRAME_WIDTH; + mvaddstr(y, x + i, FRAME[9]); + } + + for (j = 0; j < menu->height; j++) { + y++; + G_start; + mvaddstr(y, x, FRAME[3]); + G_end; + draw_menu_item(menu, menu->offset + j); + G_start; + mvaddstr(y, x + w - FRAME_WIDTH, FRAME[5]); + G_end; + } + y++; + if (menu->offset + menu->height == menu->nitem) { + G_start; + mvaddstr(y, x, FRAME[6]); + for (i = FRAME_WIDTH; i < w - FRAME_WIDTH; i += FRAME_WIDTH) + mvaddstr(y, x + i, FRAME[7]); + mvaddstr(y, x + i, FRAME[8]); + G_end; + } + else { + G_start; + mvaddstr(y, x, FRAME[3]); + G_end; + for (i = FRAME_WIDTH; i < w - FRAME_WIDTH; i += FRAME_WIDTH) + mvaddstr(y, x + i, FRAME[4]); + G_start; + mvaddstr(y, x + i, FRAME[5]); + G_end; + i = (w / 2 - 1) / FRAME_WIDTH * FRAME_WIDTH; + mvaddstr(y, x + i, FRAME[10]); + } +} + +void +draw_menu_item(Menu * menu, int select) +{ + mvaddnstr(menu->y + select - menu->offset, menu->x, + menu->item[select].label, menu->width); +} + +int +select_menu(Menu * menu, int select) +{ + if (select < 0 || select >= menu->nitem) + return (MENU_NOTHING); + if (select < menu->offset) + up_menu(menu, menu->offset - select); + else if (select >= menu->offset + menu->height) + down_menu(menu, select - menu->offset - menu->height + 1); + + if (menu->select >= menu->offset && + menu->select < menu->offset + menu->height) + draw_menu_item(menu, menu->select); + menu->select = select; + standout(); + draw_menu_item(menu, menu->select); + standend(); +/* + * move(menu->cursorY, menu->cursorX); */ + move(menu->y + select - menu->offset, menu->x); + toggle_stand(); + refresh(); + + return (menu->select); +} + +void +goto_menu(Menu * menu, int select, int down) +{ + int select_in; + if (select >= menu->nitem) + select = menu->nitem - 1; + else if (select < 0) + select = 0; + select_in = select; + while (menu->item[select].type == MENU_NOP) { + if (down > 0) { + if (++select >= menu->nitem) + { + down_menu(menu, select_in - menu->select); + select = menu->select; + break; + } + } + else if (down < 0) { + if (--select < 0) + { + up_menu(menu, menu->select - select_in); + select = menu->select; + break; + } + } + else { + return; + } + } + select_menu(menu, select); +} + +void +up_menu(Menu * menu, int n) +{ + if (n < 0 || menu->offset == 0) + return; + menu->offset -= n; + if (menu->offset < 0) + menu->offset = 0; + + draw_menu(menu); +} + +void +down_menu(Menu * menu, int n) +{ + if (n < 0 || menu->offset + menu->height == menu->nitem) + return; + menu->offset += n; + if (menu->offset + menu->height > menu->nitem) + menu->offset = menu->nitem - menu->height; + + draw_menu(menu); +} + +int +action_menu(Menu * menu) +{ + char c; + int select; + MenuItem item; + + if (menu->active == 0) { + if (menu->parent != NULL) + menu->parent->active = 0; + return (0); + } + draw_all_menu(menu); + select_menu(menu, menu->select); + + while (1) { +#ifdef MOUSE + if (use_mouse) + mouse_active(); +#endif /* MOUSE */ + c = getch(); +#ifdef MOUSE + if (use_mouse) + mouse_inactive(); +#if defined(USE_GPM) || defined(USE_SYSMOUSE) + if (c == X_MOUSE_SELECTED) { + select = X_Mouse_Selection; + if (select != MENU_NOTHING) + break; + } +#endif /* defined(USE_GPM) || * * * * * * + * defined(USE_SYSMOUSE) */ +#endif /* MOUSE */ + if (IS_ASCII(c)) { /* Ascii */ + select = (*menu->keymap[(int) c]) (c); + if (select != MENU_NOTHING) + break; + } + } + if (select >= 0 && select < menu->nitem) { + item = menu->item[select]; + if (item.type & MENU_POPUP) { + popup_menu(menu, item.popup); + return (1); + } + if (menu->parent != NULL) + menu->parent->active = 0; + if (item.type & MENU_VALUE) + *item.variable = item.value; + if (item.type & MENU_FUNC) { + CurrentKey = -1; + CurrentKeyData = NULL; + CurrentMenuData = item.data; + (*item.func) (); + CurrentMenuData = NULL; + } + } + else if (select == MENU_CLOSE) { + if (menu->parent != NULL) + menu->parent->active = 0; + } + return (0); +} + +void +popup_menu(Menu * parent, Menu * menu) +{ + int active = 1; + + if (menu->item == NULL || menu->nitem == 0) + return; + if (menu->active) + return; + +#ifdef MOUSE +#ifdef USE_GPM + gpm_handler = gpm_process_menu_mouse; +#endif /* USE_GPM */ +#ifdef USE_SYSMOUSE + sysm_handler = sysm_process_menu_mouse; +#endif /* USE_SYSMOUSE */ +#endif /* MOUSE */ + menu->parent = parent; + menu->select = menu->initial; + menu->offset = 0; + menu->active = 1; + if (parent != NULL) { + menu->cursorX = parent->cursorX; + menu->cursorY = parent->cursorY; + guess_menu_xy(parent, menu->width, &menu->x, &menu->y); + } + geom_menu(menu, menu->x, menu->y, menu->select); + + CurrentMenu = menu; + while (active) { + active = action_menu(CurrentMenu); + displayBuffer(Currentbuf, B_FORCE_REDRAW); + } + menu->active = 0; + CurrentMenu = parent; +#ifdef MOUSE +#ifdef USE_GPM + if (CurrentMenu == NULL) + gpm_handler = gpm_process_mouse; +#endif /* USE_GPM */ +#ifdef USE_SYSMOUSE + if (CurrentMenu == NULL) + sysm_handler = sysm_process_mouse; +#endif /* USE_SYSMOUSE */ +#endif /* MOUSE */ +} + +void +guess_menu_xy(Menu * parent, int width, int *x, int *y) +{ + *x = parent->x + parent->width + FRAME_WIDTH - 1; + if (*x + width + FRAME_WIDTH > COLS) { + *x = COLS - width - FRAME_WIDTH; + if ((parent->x + parent->width / 2 > *x) && + (parent->x + parent->width / 2 > COLS / 2)) + *x = parent->x - width - FRAME_WIDTH + 1; + } + *y = parent->y + parent->select - parent->offset; +} + +void +new_option_menu(Menu * menu, char **label, int *variable, void (*func) ()) +{ + int i, nitem; + char **p; + MenuItem *item; + + if (label == NULL || *label == NULL) + return; + + for (i = 0, p = label; *p != NULL; i++, p++); + nitem = i; + + item = New_N(MenuItem, nitem + 1); + + for (i = 0, p = label; i < nitem; i++, p++) { + if (func != NULL) + item[i].type = MENU_VALUE | MENU_FUNC; + else + item[i].type = MENU_VALUE; + item[i].label = *p; + item[i].variable = variable; + item[i].value = i; + item[i].func = func; + item[i].popup = NULL; + item[i].keys = ""; + } + item[nitem].type = MENU_END; + + new_menu(menu, item); +} + +/* --- MenuFunctions --- */ + +#ifdef __EMX__ +static int +mPc(char c) +{ + c = getch(); + return(MenuPcKeymap[(int)c](c)); +} +#endif + +static int +mEsc(char c) +{ + c = getch(); + return (MenuEscKeymap[(int) c] (c)); +} + +static int +mEscB(char c) +{ + c = getch(); + if (IS_DIGIT(c)) + return (mEscD(c)); + else + return (MenuEscBKeymap[(int) c] (c)); +} + +static int +mEscD(char c) +{ + int d; + + d = (int) c - (int) '0'; + c = getch(); + if (IS_DIGIT(c)) { + d = d * 10 + (int) c - (int) '0'; + c = getch(); + } + if (c == '~') + return (MenuEscDKeymap[d] (c)); + else + return (MENU_NOTHING); +} + +static int +mNull(char c) +{ + return (MENU_NOTHING); +} + +static int +mSelect(char c) +{ + if (IS_ASCII(c)) + return (select_menu(CurrentMenu, CurrentMenu->keyselect[(int) c])); + else + return (MENU_NOTHING); +} + +static int +mDown(char c) +{ + if (CurrentMenu->select >= CurrentMenu->nitem - 1) + return (MENU_NOTHING); + goto_menu(CurrentMenu, CurrentMenu->select + 1, 1); + return (MENU_NOTHING); +} + +static int +mUp(char c) +{ + if (CurrentMenu->select <= 0) + return (MENU_NOTHING); + goto_menu(CurrentMenu, CurrentMenu->select - 1, -1); + return (MENU_NOTHING); +} + +static int +mLast(char c) +{ + goto_menu(CurrentMenu, CurrentMenu->nitem - 1, -1); + return (MENU_NOTHING); +} + +static int +mTop(char c) +{ + goto_menu(CurrentMenu, 0, 1); + return (MENU_NOTHING); +} + +static int +mNext(char c) +{ + int select = CurrentMenu->select + CurrentMenu->height; + + if (select >= CurrentMenu->nitem) + return mLast(c); + down_menu(CurrentMenu, CurrentMenu->height); + goto_menu(CurrentMenu, select, -1); + return (MENU_NOTHING); +} + +static int +mPrev(char c) +{ + int select = CurrentMenu->select - CurrentMenu->height; + + if (select < 0) + return mTop(c); + up_menu(CurrentMenu, CurrentMenu->height); + goto_menu(CurrentMenu, select, 1); + return (MENU_NOTHING); +} + +static int +mFore(char c) +{ + if (CurrentMenu->select >= CurrentMenu->nitem - 1) + return (MENU_NOTHING); + goto_menu(CurrentMenu, (CurrentMenu->select + CurrentMenu->height - 1), (CurrentMenu->height + 1)); + return (MENU_NOTHING); +} + +static int +mBack(char c) +{ + if (CurrentMenu->select <= 0) + return (MENU_NOTHING); + goto_menu(CurrentMenu, (CurrentMenu->select - CurrentMenu->height + 1), (-1 - CurrentMenu->height)); + return (MENU_NOTHING); +} + +static int +mOk(char c) +{ + int select = CurrentMenu->select; + + if (CurrentMenu->item[select].type == MENU_NOP) + return (MENU_NOTHING); + return (select); +} + +static int +mCancel(char c) +{ + return (MENU_CANCEL); +} + +static int +mClose(char c) +{ + return (MENU_CLOSE); +} + +static int +mSusp(char c) +{ + susp(); + draw_all_menu(CurrentMenu); + select_menu(CurrentMenu, CurrentMenu->select); + return (MENU_NOTHING); +} + +static char *SearchString = NULL; + +int (*menuSearchRoutine) (Menu *, char *, int); + +static int +menuForwardSearch (Menu* menu, char* str, int from) +{ + int i; + char* p; + if ((p = regexCompile (str, IgnoreCase)) != NULL) { + message (p, 0, 0); + return -1; + } + for (i = from; i < menu->nitem; i++) + if (regexMatch (menu->item[i].label, 0, 0) == 1) + return i; + return -1; +} + +int +menu_search_forward (Menu* menu, int from) +{ + char *str; + int found; + str = inputStrHist("Forward: ", NULL, TextHist); + if (str != NULL && *str == '\0') + str = SearchString; + if (str == NULL || *str == '\0') + return (MENU_NOTHING); + SearchString = str; + found = menuForwardSearch (menu, SearchString, from); + if (WrapSearch && found == -1) + found = menuForwardSearch (menu, SearchString, 0); + menuSearchRoutine = menuForwardSearch; + if (found >= 0) + return found; + return from; +} + +static int +mSrchF (char c) +{ + int select; + select = menu_search_forward (CurrentMenu, CurrentMenu->select); + goto_menu (CurrentMenu, select, 1); + return (MENU_NOTHING); +} + +static int +menuBackwardSearch (Menu* menu, char* str, int from) +{ + int i; + char* p; + if ((p = regexCompile (str, IgnoreCase)) != NULL) { + message (p, 0, 0); + return -1; + } + for (i = from; i >= 0 ; i--) + if (regexMatch (menu->item[i].label, 0, 0) == 1) + return i; + return -1; +} + +int +menu_search_backward (Menu* menu, int from) +{ + char *str; + int found; + str = inputStrHist("Backward: ", NULL, TextHist); + if (str != NULL && *str == '\0') + str = SearchString; + if (str == NULL || *str == '\0') + return (MENU_NOTHING); + SearchString = str; + found = menuBackwardSearch (menu, SearchString, from); + if (WrapSearch && found == -1) + found = menuBackwardSearch (menu, SearchString, 0); + menuSearchRoutine = menuBackwardSearch; + if (found >= 0) + return found; + return from; +} + +static int +mSrchB (char c) +{ + int select; + select = menu_search_backward (CurrentMenu, CurrentMenu->select); + goto_menu (CurrentMenu, select, -1); + return (MENU_NOTHING); +} + +static int +menu_search_next_previous (Menu* menu, int from, int reverse) +{ + int found; + int new_from; + static int (*routine[2]) (Menu *, char *, int) = + { + menuForwardSearch, menuBackwardSearch + }; + + if (menuSearchRoutine == NULL) { + disp_message ("No previous regular expression", TRUE); + return from; + } + addstr(menuSearchRoutine == menuForwardSearch ? "Forward: " : "Backward: "); + addstr(SearchString); + if (reverse != 0) + reverse = 1; + if (menuSearchRoutine == menuBackwardSearch) + reverse ^= 1; + new_from = from - reverse * 2 + 1; + if (new_from >=0 && new_from < menu->nitem) + found = (*routine[reverse]) (menu, SearchString, new_from); + else + found = (*routine[reverse]) (menu, SearchString, from); + if (WrapSearch && found == -1) { + found = (*routine[reverse]) (menu, SearchString, reverse * menu->nitem); + } + if (found >= 0) + return found; + return from; +} + +static int +mSrchN (char c) +{ + int select; + select = menu_search_next_previous (CurrentMenu, CurrentMenu->select, 0); + goto_menu (CurrentMenu, select, 1); + return (MENU_NOTHING); +} + +static int +mSrchP (char c) +{ + int select; + select = menu_search_next_previous (CurrentMenu, CurrentMenu->select, 1); + goto_menu (CurrentMenu, select, -1); + return (MENU_NOTHING); +} + +#ifdef MOUSE +#define MOUSE_BTN1_DOWN 0 +#define MOUSE_BTN2_DOWN 1 +#define MOUSE_BTN3_DOWN 2 +#define MOUSE_BTN4_DOWN_RXVT 3 +#define MOUSE_BTN5_DOWN_RXVT 4 +#define MOUSE_BTN4_DOWN_XTERM 64 +#define MOUSE_BTN5_DOWN_XTERM 65 +#define MOUSE_BTN_UP 3 +#define MOUSE_BTN_RESET -1 +#define MOUSE_SCROLL_LINE 5 + +static int +process_mMouse(int btn, int x, int y) +{ + Menu *menu; + int select; + static int press_btn = MOUSE_BTN_RESET, press_x, press_y; + char c = ' '; + + menu = CurrentMenu; + + if (x < 0 || x >= COLS || y < 0 || y > LASTLINE) + return (MENU_NOTHING); + + if (btn == MOUSE_BTN_UP) { + if (press_btn == MOUSE_BTN1_DOWN || + press_btn == MOUSE_BTN3_DOWN) { + if (x < menu->x - FRAME_WIDTH || + x >= menu->x + menu->width + FRAME_WIDTH || + y < menu->y - 1 || + y >= menu->y + menu->height + 1) { + return (MENU_CANCEL); + } + else if ((x >= menu->x - FRAME_WIDTH && + x < menu->x) || + (x >= menu->x + menu->width && + x < menu->x + menu->width + FRAME_WIDTH)) { + return (MENU_NOTHING); + } + else if (y == menu->y - 1) { + mPrev(c); + return (MENU_NOTHING); + } + else if (y == menu->y + menu->height) { + mNext(c); + return (MENU_NOTHING); + } + else { + select = y - menu->y + menu->offset; + if (menu->item[select].type == MENU_NOP) + return (MENU_NOTHING); + return (select_menu(menu, select)); + } + } + } + else { + press_btn = btn; + press_x = x; + press_y = y; + } + return (MENU_NOTHING); +} + +static int +mMouse(char c) +{ + int btn, x, y; + + btn = (unsigned char) getch() - 32; + x = (unsigned char) getch() - 33; + if (x < 0) + x += 0x100; + y = (unsigned char) getch() - 33; + if (y < 0) + y += 0x100; + + /* + * if (x < 0 || x >= COLS || y < 0 || y > LASTLINE) return; */ + return process_mMouse(btn, x, y); +} + +#ifdef USE_GPM +static int +gpm_process_menu_mouse(Gpm_Event * event, void *data) +{ + int btn, x, y; + if (event->type & GPM_UP) + btn = MOUSE_BTN_UP; + else if (event->type & GPM_DOWN) { + switch (event->buttons) { + case GPM_B_LEFT: + btn = MOUSE_BTN1_DOWN; + break; + case GPM_B_MIDDLE: + btn = MOUSE_BTN2_DOWN; + break; + case GPM_B_RIGHT: + btn = MOUSE_BTN3_DOWN; + break; + } + } + else { + GPM_DRAWPOINTER(event); + return 0; + } + x = event->x; + y = event->y; + X_Mouse_Selection = process_mMouse(btn, x - 1, y - 1); + return X_MOUSE_SELECTED; +} +#endif /* USE_GPM */ + +#ifdef USE_SYSMOUSE +static int +sysm_process_menu_mouse(int x, int y, int nbs, int obs) +{ + int btn; + int bits; + + if (obs & ~nbs) + btn = MOUSE_BTN_UP; + else if (nbs & ~obs) { + bits = nbs & ~obs; + btn = bits & 0x1 ? MOUSE_BTN1_DOWN : + (bits & 0x2 ? MOUSE_BTN2_DOWN : + (bits & 0x4 ? MOUSE_BTN3_DOWN : 0)); + } + else /* nbs == obs */ + return 0; + X_Mouse_Selection = process_mMouse(btn, x, y); + return X_MOUSE_SELECTED; +} +#endif /* USE_SYSMOUSE */ +#else /* not MOUSE */ +static int +mMouse(char c) +{ + return (MENU_NOTHING); +} +#endif /* not MOUSE */ + +/* --- MenuFunctions (END) --- */ + +/* --- MainMenu --- */ + +void +popupMenu(int x, int y, Menu *menu) +{ + initSelectMenu(); + + menu->cursorX = Currentbuf->cursorX; + menu->cursorY = Currentbuf->cursorY; + menu->x = x + FRAME_WIDTH + 1; + menu->y = y + 2; + + popup_menu(NULL, menu); +} + +void +mainMenu(int x, int y) +{ + popupMenu(x, y, &MainMenu); +} + +void +mainMn(void) +{ + Menu *menu = &MainMenu; + char *data; + int n; + + data = searchKeyData(); + if (data != NULL) { + n = getMenuN(w3mMenuList, data); + if (n < 0) + return; + menu = w3mMenuList[n].menu; + } + popupMenu(Currentbuf->cursorX, Currentbuf->cursorY, menu); +} + +/* --- MainMenu (END) --- */ + +/* --- SelectMenu --- */ + +static void +initSelectMenu(void) +{ + int i, nitem, len = 0, l; + Buffer *buf; + Str str; + char **label; + static char *comment = " SPC for select / D for delete buffer "; + + SelectV = -1; + for (i = 0, buf = Firstbuf; buf != NULL; i++, buf = buf->nextBuffer) { + if (buf == Currentbuf) + SelectV = i; + } + nitem = i; + + label = New_N(char *, nitem + 2); + for (i = 0, buf = Firstbuf; i < nitem; i++, buf = buf->nextBuffer) { + str = Sprintf("<%s>", buf->buffername); + if (buf->filename != NULL) { + switch (buf->currentURL.scheme) { + case SCM_LOCAL: + case SCM_LOCAL_CGI: + if (strcmp(buf->currentURL.file, "-")) { + Strcat_char(str, ' '); + Strcat_charp(str, buf->filename); + } + break; + case SCM_UNKNOWN: + case SCM_MISSING: + break; + default: + Strcat_char(str, ' '); + Strcat(str, parsedURL2Str(&buf->currentURL)); + break; + } + } + label[i] = str->ptr; + if (len < str->length) + len = str->length; + } + l = strlen(comment); + if (len < l + 4) + len = l + 4; + if (len > COLS - 2 * FRAME_WIDTH) + len = COLS - 2 * FRAME_WIDTH; + len = (len > 1) ? ((len - l + 1) / 2) : 0; + str = Strnew(); + for (i = 0; i < len; i++) + Strcat_char(str, '-'); + Strcat_charp(str, comment); + for (i = 0; i < len; i++) + Strcat_char(str, '-'); + label[nitem] = str->ptr; + label[nitem + 1] = NULL; + + new_option_menu(&SelectMenu, label, &SelectV, smChBuf); + SelectMenu.initial = SelectV; + SelectMenu.cursorX = Currentbuf->cursorX; + SelectMenu.cursorY = Currentbuf->cursorY; + SelectMenu.keymap['D'] = smDelBuf; + SelectMenu.item[nitem].type = MENU_NOP; +} + +static void +smChBuf(void) +{ + int i; + Buffer *buf; + + if (SelectV < 0 || SelectV >= SelectMenu.nitem) + return; + for (i = 0, buf = Firstbuf; i < SelectV; i++, buf = buf->nextBuffer); + Currentbuf = buf; + if (clear_buffer) { + for (buf = Firstbuf; buf != NULL; buf = buf->nextBuffer) + tmpClearBuffer(buf); + } +} + +static int +smDelBuf(char c) { + int i, x, y, select; + Buffer *buf; + + if (CurrentMenu->select < 0 || CurrentMenu->select >= SelectMenu.nitem) + return (MENU_NOTHING); + for (i = 0, buf = Firstbuf; i < CurrentMenu->select; i++, buf = buf->nextBuffer); + if (Currentbuf == buf) + Currentbuf = buf->nextBuffer; + Firstbuf = deleteBuffer(Firstbuf, buf); + if (!Currentbuf) + Currentbuf = nthBuffer(Firstbuf, i - 1);; + if (Firstbuf == NULL) { + Firstbuf = nullBuffer(); + Currentbuf = Firstbuf; + } + + x = CurrentMenu->x; + y = CurrentMenu->y; + select = CurrentMenu->select; + + initSelectMenu(); + + CurrentMenu->x = x; + CurrentMenu->y = y; + + geom_menu(CurrentMenu, x, y, 0); + + CurrentMenu->select = (select <= CurrentMenu->nitem - 2) ? select + : (CurrentMenu->nitem - 2); + + displayBuffer(Currentbuf, B_FORCE_REDRAW); + draw_all_menu(CurrentMenu); + select_menu(CurrentMenu, CurrentMenu->select); + return (MENU_NOTHING); +} + +/* --- SelectMenu (END) --- */ + +/* --- OptionMenu --- */ + +void +optionMenu(int x, int y, char **label, int *variable, int initial, void (*func) ()) +{ + Menu menu; + + new_option_menu(&menu, label, variable, func); + menu.cursorX = COLS - 1; + menu.cursorY = LASTLINE; + menu.x = x; + menu.y = y; + menu.initial = initial; + + popup_menu(NULL, &menu); +} + +/* --- OptionMenu (END) --- */ + +/* --- InitMenu --- */ + +void +initMenu(void) +{ + FILE *mf; + Str line; + char *p, *s; + int in_menu, nmenu, nitem, type; + MenuItem *item; + MenuList *list; + + w3mMenuList = New_N(MenuList, 3); + w3mMenuList[0].id = "Main"; + w3mMenuList[0].menu = &MainMenu; + w3mMenuList[0].item = MainMenuItem; + w3mMenuList[1].id = "Select"; + w3mMenuList[1].menu = &SelectMenu; + w3mMenuList[1].item = NULL; + w3mMenuList[2].id = NULL; + + if ((mf = fopen(rcFile(MENU_FILE), "rt")) == NULL) + goto create_menu; + + if (!w3mNFuncList) + w3mNFuncList = countFuncList(w3mFuncList); + + in_menu = 0; + while (!feof(mf)) { + line = Strfgets(mf); + Strchop(line); + Strremovefirstspaces(line); + if (line->length == 0) + continue; + p = line->ptr; + s = getWord(&p); + if (*s == '#') /* comment */ + continue; + if (in_menu) { + type = setMenuItem(&item[nitem], s, p); + if (type == -1) + continue; /* error */ + if (type == MENU_END) + in_menu = 0; + else { + nitem++; + item = New_Reuse(MenuItem, item, (nitem + 1)); + w3mMenuList[nmenu].item = item; + item[nitem].type = MENU_END; + } + } + else { + if (strcmp(s, "menu")) /* error */ + continue; + s = getQWord(&p); + if (*s == '\0') /* error */ + continue; + in_menu = 1; + if ((nmenu = getMenuN(w3mMenuList, s)) != -1) + w3mMenuList[nmenu].item = New(MenuItem); + else + nmenu = addMenuList(&w3mMenuList, s); + item = w3mMenuList[nmenu].item; + nitem = 0; + item[nitem].type = MENU_END; + } + } + fclose(mf); + + create_menu: + for (list = w3mMenuList; list->id != NULL; list++) { + if (list->item == NULL) + continue; + new_menu(list->menu, list->item); + } +} + +int +setMenuItem(MenuItem * item, char *type, char *line) +{ + char *label, *func, *popup, *keys, *data; + int f; + int n; + + if (type == NULL || *type == '\0') /* error */ + return -1; + if (strcmp(type, "end") == 0) { + item->type = MENU_END; + return MENU_END; + } + else if (strcmp(type, "nop") == 0) { + item->type = MENU_NOP; + item->label = getQWord(&line); + return MENU_NOP; + } + else if (strcmp(type, "func") == 0) { + label = getQWord(&line); + func = getWord(&line); + keys = getQWord(&line); + data = getQWord(&line); + if (*func == '\0') /* error */ + return -1; + item->type = MENU_FUNC; + item->label = label; + f = getFuncList(func, w3mFuncList, w3mNFuncList); + item->func = w3mFuncList[(f >= 0) ? f : FUNCNAME_nulcmd].func; + item->keys = keys; + item->data = data; + return MENU_FUNC; + } + else if (strcmp(type, "popup") == 0) { + label = getQWord(&line); + popup = getQWord(&line); + keys = getQWord(&line); + if (*popup == '\0') /* error */ + return -1; + item->type = MENU_POPUP; + item->label = label; + if ((n = getMenuN(w3mMenuList, popup)) == -1) + n = addMenuList(&w3mMenuList, popup); + item->popup = w3mMenuList[n].menu; + item->keys = keys; + return MENU_POPUP; + } + return -1; /* error */ +} + +int +addMenuList(MenuList ** mlist, char *id) +{ + int n; + MenuList *list = *mlist; + + for (n = 0; list->id != NULL; list++, n++); + *mlist = New_Reuse(MenuList, *mlist, (n + 2)); + list = *mlist + n; + list->id = id; + list->menu = New(Menu); + list->item = New(MenuItem); + (list + 1)->id = NULL; + return n; +} + +int +getMenuN(MenuList * list, char *id) +{ + int n; + + for (n = 0; list->id != NULL; list++, n++) { + if (strcmp(id, list->id) == 0) + return n; + } + return -1; +} + +/* --- InitMenu (END) --- */ + +#endif /* MENU */ @@ -0,0 +1,53 @@ +/* + * w3m menu.h + */ + +#ifndef MENU_H +#define MENU_H + +#define MENU_END 0 +#define MENU_NOP 1 +#define MENU_VALUE 2 +#define MENU_FUNC 4 +#define MENU_POPUP 8 + +#define MENU_NOTHING -1 +#define MENU_CANCEL -2 +#define MENU_CLOSE -3 + +typedef struct _MenuItem { + int type; + char *label; + int *variable; + int value; + void (*func) (); + struct _Menu *popup; + char *keys; + char *data; +} MenuItem; + +typedef struct _Menu { + struct _Menu *parent; + int cursorX; + int cursorY; + int x; + int y; + int width; + int height; + int nitem; + MenuItem *item; + int initial; + int select; + int offset; + int active; + int (*keymap[128]) (char c); + int keyselect[128]; +} Menu; + +typedef struct _MenuList { + char *id; + Menu *menu; + MenuItem *item; +} MenuList; + +#endif /* not MENU_H */ diff --git a/mimehead.c b/mimehead.c new file mode 100644 index 0000000..90dcceb --- /dev/null +++ b/mimehead.c @@ -0,0 +1,306 @@ + +/* + * MIME header support by Akinori ITO + */ + +#include <sys/types.h> +#include "Str.h" + +#define LINELEN 4096 + +#define MIME_ENCODED_LINE_LIMIT 80 +#define MIME_ENCODED_WORD_LENGTH_OFFSET 18 +#define MIME_ENCODED_WORD_LENGTH_ESTIMATION(x) \ + (((x)+2)*4/3+MIME_ENCODED_WORD_LENGTH_OFFSET) +#define MIME_DECODED_WORD_LENGTH_ESTIMATION(x) \ + (((x)-MIME_ENCODED_WORD_LENGTH_OFFSET)/4*3) +#define J_CHARSET "ISO-2022-JP" + +#define BAD_BASE64 255 + +static +unsigned char +c2e(char x) +{ + if ('A' <= x && x <= 'Z') + return (x) - 'A'; + if ('a' <= x && x <= 'z') + return (x) - 'a' + 26; + if ('0' <= x && x <= '9') + return (x) - '0' + 52; + if (x == '+') + return 62; + if (x == '/') + return 63; + return BAD_BASE64; +} + +static +int +ha2d(char x, char y) +{ + int r = 0; + + if ('0' <= x && x <= '9') + r = x - '0'; + else if ('A' <= x && x <= 'F') + r = x - 'A' + 10; + else if ('a' <= x && x <= 'f') + r = x - 'a' + 10; + + r <<= 4; + + if ('0' <= y && y <= '9') + r += y - '0'; + else if ('A' <= y && y <= 'F') + r += y - 'A' + 10; + else if ('a' <= y && y <= 'f') + r += y - 'a' + 10; + + return r; + +} + +Str +decodeB(char **ww) +{ + unsigned char c[4]; + char *wp = *ww; + char d[3]; + int i, n_pad; + Str ap = Strnew_size(strlen(wp)); + + n_pad = 0; + while (1) { + for (i = 0; i < 4; i++) { + c[i] = *(wp++); + if (*wp == '\0' || *wp == '?') { + i++; + for (; i < 4; i++) { + c[i] = '='; + } + break; + } + } + if (c[3] == '=') { + n_pad++; + c[3] = 'A'; + if (c[2] == '=') { + n_pad++; + c[2] = 'A'; + } + } + for (i = 0; i < 4; i++) { + c[i] = c2e(c[i]); + if (c[i] == BAD_BASE64) { + *ww = wp; + return ap; + } + } + d[0] = ((c[0] << 2) | (c[1] >> 4)); + d[1] = ((c[1] << 4) | (c[2] >> 2)); + d[2] = ((c[2] << 6) | c[3]); + for (i = 0; i < 3 - n_pad; i++) { + Strcat_char(ap, d[i]); + } + if (n_pad || *wp == '\0' || *wp == '?') + break; + } + *ww = wp; + return ap; +} + +/* RFC2047 (4.2. The "Q" encoding) */ +Str +decodeQ(char **ww) +{ + char *w = *ww; + Str a = Strnew_size(strlen(w)); + + for (; *w != '\0' && *w != '?'; w++) { + if (*w == '=') { + w++; + Strcat_char(a, ha2d(*w, *(w + 1))); + w++; + } + else if (*w == '_') { + Strcat_char(a, ' '); + } + else + Strcat_char(a, *w); + } + *ww = w; + return a; +} + +/* RFC2045 (6.7. Quoted-Printable Content-Transfer-Encoding) */ +Str +decodeQP(char **ww) +{ + char *w = *ww; + Str a = Strnew_size(strlen(w)); + + for (; *w != '\0'; w++) { + if (*w == '=') { + w++; + if (*w == '\n' || *w == '\r' || *w == ' ' || *w == '\t') { + while (*w != '\n' && *w != '\0') + w++; + if (*w == '\0') + break; + } + else { + if (*w == '\0' || *(w + 1) == '\0') + break; + Strcat_char(a, ha2d(*w, *(w + 1))); + w++; + } + } + else + Strcat_char(a, *w); + } + *ww = w; + return a; +} + +Str +decodeWord(char **ow) +{ + char charset[32]; + char *p, *w = *ow; + char method; + Str a = Strnew(); + + if (*w != '=' || *(w + 1) != '?') + goto convert_fail; + w += 2; + for (p = charset; *w != '?'; w++) { + if (*w == '\0') + goto convert_fail; + *(p++) = *w; + } + *p = '\0'; + if (strcasecmp(charset, J_CHARSET) != 0) { + /* NOT ISO-2022-JP encoding ... don't convert */ + goto convert_fail; + } + w++; + method = *(w++); + if (*w != '?') + goto convert_fail; + w++; + p = w; + switch (method) { + case 'B': + a = decodeB(&w); + break; + case 'Q': + a = decodeQ(&w); + break; + default: + goto convert_fail; + } + if (p == w) + goto convert_fail; + if (*w == '?') { + w++; + if (*w == '=') + w++; + } + *ow = w; + return a; + + convert_fail: + return Strnew(); +} + +/* + * convert MIME encoded string to the original one + */ +Str +decodeMIME(char *orgstr) +{ + char *org = orgstr; + char *org0, *p; + Str cnv = Strnew_size(strlen(orgstr)); + + while (*org) { + if (*org == '=' && *(org + 1) == '?') { + nextEncodeWord: + p = org; + Strcat(cnv, decodeWord(&org)); + if (org == p) { /* Convert failure */ + Strcat_charp(cnv, org); + return cnv; + } + org0 = org; + SPCRLoop: + switch (*org0) { + case ' ': + case '\t': + case '\n': + case '\r': + org0++; + goto SPCRLoop; + case '=': + if (org0[1] == '?') { + org = org0; + goto nextEncodeWord; + } + default: + break; + } + } + else + Strcat_char(cnv, *(org++)); + } + return cnv; +} + +/* encoding */ + +static char Base64Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +Str +encodeB(char *a) +{ + unsigned char d[3]; + unsigned char c1, c2, c3, c4; + int i, n_pad; + Str w = Strnew(); + + while (1) { + if (*a == '\0') + break; + n_pad = 0; + d[1] = d[2] = 0; + for (i = 0; i < 3; i++) { + d[i] = a[i]; + if (a[i] == '\0') { + n_pad = 3 - i; + break; + } + } + c1 = d[0] >> 2; + c2 = (((d[0] << 4) | (d[1] >> 4)) & 0x3f); + if (n_pad == 2) { + c3 = c4 = 64; + } + else if (n_pad == 1) { + c3 = ((d[1] << 2) & 0x3f); + c4 = 64; + } + else { + c3 = (((d[1] << 2) | (d[2] >> 6)) & 0x3f); + c4 = (d[2] & 0x3f); + } + Strcat_char(w, Base64Table[c1]); + Strcat_char(w, Base64Table[c2]); + Strcat_char(w, Base64Table[c3]); + Strcat_char(w, Base64Table[c4]); + if (n_pad) + break; + a += 3; + } + return w; +} diff --git a/mktable.c b/mktable.c new file mode 100644 index 0000000..0c87f28 --- /dev/null +++ b/mktable.c @@ -0,0 +1,115 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include "hash.h" +#include "Str.h" +#include <gc.h> + +#include "gcmain.c" + +defhash(HashItem_ss *, int, hss_i) +#define keycomp(x,y) ((x)==(y)) + + static unsigned int hashfunc(HashItem_ss * x) +{ + return (unsigned int) x; +} + +defhashfunc(HashItem_ss *, int, hss_i) + int + MAIN(int argc, char *argv[], char **envp) +{ + FILE *f; + Hash_ss *hash; + HashItem_ss **hashitems, *hi; + int size, n, i, j; + Str s, name, fbase; + char *p; + Hash_hss_i *rhash; + + if (argc != 3) { + fprintf(stderr, "usage: %s hashsize file.tab > file.c\n", argv[0]); + exit(1); + } + size = atoi(argv[1]); + if (size <= 0) { + fprintf(stderr, "hash size should be positive\n"); + exit(1); + } + if ((f = fopen(argv[2], "r")) == NULL) { + fprintf(stderr, "Can't open %s\n", argv[2]); + exit(1); + } + fbase = Strnew_charp(argv[2]); + if (strchr(fbase->ptr, '.')) + while (Strlastchar(fbase) != '.') + Strshrink(fbase, 1); + Strshrink(fbase, 1); + + hash = newHash_ss(size); + printf("#include \"hash.h\"\n"); + for (;;) { + s = Strfgets(f); + if (s->length == 0) + exit(0); + Strremovetrailingspaces(s); + if (Strcmp_charp(s, "%%") == 0) + break; + puts(s->ptr); + } + n = 0; + for (;;) { + s = Strfgets(f); + if (s->length == 0) + break; + Strremovefirstspaces(s); + Strremovetrailingspaces(s); + name = Strnew(); + for (p = s->ptr; *p; p++) { + if (isspace(*p)) + break; + Strcat_char(name, *p); + } + while (*p && isspace(*p)) + p++; + putHash_ss(hash, name->ptr, p); + n++; + } + fclose(f); + + hashitems = (HashItem_ss **) GC_malloc(sizeof(HashItem_ss *) * n); + rhash = newHash_hss_i(n * 2); + j = 0; + for (i = 0; i < hash->size; i++) { + for (hi = hash->tab[i]; hi != NULL; hi = hi->next) { + hashitems[j] = hi; + putHash_hss_i(rhash, hi, j); + j++; + } + } + printf("static HashItem_si MyHashItem[] = {\n"); + for (i = 0; i < j; i++) { + printf(" /* %d */ {\"%s\",%s,", i, + hashitems[i]->key, + hashitems[i]->value); + if (hashitems[i]->next == NULL) { + printf("NULL},\n"); + } + else { + printf("&MyHashItem[%d]},\n", getHash_hss_i(rhash, hashitems[i]->next, -1)); + } + } + printf("};\n\nstatic HashItem_si *MyHashItemTbl[] = {\n"); + + for (i = 0; i < hash->size; i++) { + if (hash->tab[i]) + printf(" &MyHashItem[%d],\n", getHash_hss_i(rhash, hash->tab[i], -1)); + else + printf(" NULL,\n"); + } + printf("};\n\n"); + printf("Hash_si %s = {%d, MyHashItemTbl};\n", fbase->ptr, hash->size); + + exit(0); +} diff --git a/myctype.c b/myctype.c new file mode 100644 index 0000000..479c25e --- /dev/null +++ b/myctype.c @@ -0,0 +1,51 @@ + +unsigned char MYCTYPE_MAP[ 0x100 ] = { +/* NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1, +/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +/* SPC ! " # $ % & ' ( ) * + , - . / */ + 18, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, +/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */ + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, +/* @ A B C D E F G H I J K L M N O */ + 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* P Q R S T U V W X Y Z [ \ ] ^ _ */ + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 16, 16, 16, 16, 16, +/* ` a b c d e f g h i j k l m n o */ + 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* p q r s t u v w x y z { | } ~ DEL */ + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 16, 16, 16, 16, 1, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + + + +unsigned char INTCTYPE_MAP[ 0x100 ] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 6, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 4, +}; + diff --git a/myctype.h b/myctype.h new file mode 100644 index 0000000..512e6e8 --- /dev/null +++ b/myctype.h @@ -0,0 +1,45 @@ +#ifndef _MYCTYPE_H +#define _MYCTYPE_H + +#define MYCTYPE_CNTRL 1 +#define MYCTYPE_SPACE 2 +#define MYCTYPE_ALPHA 4 +#define MYCTYPE_DIGIT 8 +#define MYCTYPE_PRINT 16 +#define MYCTYPE_ALNUM (MYCTYPE_ALPHA|MYCTYPE_DIGIT) + +#define GET_MYCTYPE(x) (MYCTYPE_MAP[(int)(unsigned char)(x)]) + +#define IS_CNTRL(x) (GET_MYCTYPE(x) & MYCTYPE_CNTRL) +#define IS_SPACE(x) (GET_MYCTYPE(x) & MYCTYPE_SPACE) +#define IS_ALPHA(x) (GET_MYCTYPE(x) & MYCTYPE_ALPHA) +#define IS_DIGIT(x) (GET_MYCTYPE(x) & MYCTYPE_DIGIT) +#define IS_PRINT(x) (GET_MYCTYPE(x) & MYCTYPE_PRINT) +#define IS_ALNUM(x) (GET_MYCTYPE(x) & MYCTYPE_ALNUM) + +extern unsigned char MYCTYPE_MAP[]; + + +#define INTCTYPE_ASCII 1 +#define INTCTYPE_ISPACE 2 +#define INTCTYPE_LATIN1 4 +#define INTCTYPE_KANJI1 8 +#define INTCTYPE_KANJI2 16 +#define INTCTYPE_KANJI (INTCTYPE_KANJI1|INTCTYPE_KANJI2) +#define INTCTYPE_INTRL INTCTYPE_ISPACE + +#define GET_INTCTYPE(x) (INTCTYPE_MAP[(int)(unsigned char)(x)]) + +#define IS_ASCII(x) (GET_INTCTYPE(x) & INTCTYPE_ASCII) +#define IS_INTSPACE(x) (GET_INTCTYPE(x) & INTCTYPE_ISPACE) +#define IS_INTERNAL(x) (GET_INTCTYPE(x) & INTCTYPE_INTRL) +#define IS_KANJI1(x) (GET_INTCTYPE(x) & INTCTYPE_KANJI1) +#define IS_KANJI2(x) (GET_INTCTYPE(x) & INTCTYPE_KANJI2) +#define IS_KANJI(x) (GET_INTCTYPE(x) & INTCTYPE_KANJI) +#define IS_LATIN1(x) (GET_INTCTYPE(x) & INTCTYPE_LATIN1) + +extern unsigned char INTCTYPE_MAP[]; + +#include <ctype.h> + +#endif diff --git a/parsetag.c b/parsetag.c new file mode 100644 index 0000000..9cafd95 --- /dev/null +++ b/parsetag.c @@ -0,0 +1,56 @@ +#include "myctype.h" +#include "indep.h" +#include "Str.h" +#include "parsetag.h" + +char * +tag_get_value(struct parsed_tagarg *t, char *arg) +{ + for (; t; t = t->next) { + if (!strcasecmp(t->arg, arg)) + return t->value; + } + return NULL; +} + +int +tag_exists(struct parsed_tagarg *t, char *arg) +{ + for (; t; t = t->next) { + if (!strcasecmp(t->arg, arg)) + return 1; + } + return 0; +} + +struct parsed_tagarg * +cgistr2tagarg(char *cgistr) +{ + Str tag; + Str value; + struct parsed_tagarg *t0, *t; + + t = t0 = NULL; + do { + t = New(struct parsed_tagarg); + t->next = t0; + t0 = t; + tag = Strnew(); + while (*cgistr && *cgistr != '=' && *cgistr != '&') + Strcat_char(tag, *cgistr++); + t->arg = form_unquote(tag)->ptr; + t->value = NULL; + if (*cgistr == '\0') + return t; + else if (*cgistr == '=') { + cgistr++; + value = Strnew(); + while (*cgistr && *cgistr != '&') + Strcat_char(value, *cgistr++); + t->value = form_unquote(value)->ptr; + } + else if (*cgistr == '&') + cgistr++; + } while (*cgistr); + return t; +} diff --git a/parsetag.h b/parsetag.h new file mode 100644 index 0000000..470645d --- /dev/null +++ b/parsetag.h @@ -0,0 +1,12 @@ +#ifndef PARSETAG_H +#define PARSETAG_H +struct parsed_tagarg { + char *arg; + char *value; + struct parsed_tagarg *next; +}; + +extern char *tag_get_value(struct parsed_tagarg *t, char *arg); +extern int tag_exists(struct parsed_tagarg *t, char *arg); +extern struct parsed_tagarg *cgistr2tagarg(char *cgistr); +#endif /* not PARSETAG_H */ diff --git a/parsetagx.c b/parsetagx.c new file mode 100644 index 0000000..712fbe8 --- /dev/null +++ b/parsetagx.c @@ -0,0 +1,273 @@ +#include "fm.h" +#include "myctype.h" +#include "indep.h" +#include "Str.h" +#include "parsetagx.h" +#include "hash.h" + +#include "html.c" + +/* parse HTML tag */ + +static int noConv(char *, char **); +static int toNumber(char *, int *); +static int toLength(char *, int *); +static int toAlign(char *, int *); +static int toVAlign(char *, int *); + +static int (*toValFunc[]) () = { + noConv, /* VTYPE_NONE */ + noConv, /* VTYPE_STR */ + toNumber, /* VTYPE_NUMBER */ + toLength, /* VTYPE_LENGTH */ + toAlign, /* VTYPE_ALIGN */ + toVAlign, /* VTYPE_VALIGN */ + noConv, /* VTYPE_ACTION */ + noConv, /* VTYPE_ENCTYPE */ + noConv, /* VTYPE_METHOD */ + noConv, /* VTYPE_MLENGTH */ + noConv, /* VTYPE_TYPE */ +}; + +static int +noConv(char *oval, char **str) +{ + *str = oval; + return 1; +} + +static int +toNumber(char *oval, int *num) +{ + *num = atoi(oval); + return 1; +} + +static int +toLength(char *oval, int *len) +{ + int w; + if (!IS_DIGIT(oval[0])) + return 0; + w = atoi(oval); + if (w < 0) + return 0; + if (w == 0) + w = 1; + if (oval[strlen(oval) - 1] == '%') + *len = -w; + else + *len = w; + return 1; +} + +static int +toAlign(char *oval, int *align) +{ + if (strcasecmp(oval, "left") == 0) + *align = ALIGN_LEFT; + else if (strcasecmp(oval, "right") == 0) + *align = ALIGN_RIGHT; + else if (strcasecmp(oval, "center") == 0) + *align = ALIGN_CENTER; + else + return 0; + return 1; +} + +static int +toVAlign(char *oval, int *valign) +{ + if (strcasecmp(oval, "top") == 0 || + strcasecmp(oval, "baseline") == 0) + *valign = VALIGN_TOP; + else if (strcasecmp(oval, "bottom") == 0) + *valign = VALIGN_BOTTOM; + else if (strcasecmp(oval, "middle") == 0) + *valign = VALIGN_MIDDLE; + else + return 0; + return 1; +} + +extern Hash_si tagtable; +#define MAX_TAG_LEN 64 + +struct parsed_tag * +parse_tag(char **s, int internal) +{ + struct parsed_tag *tag = NULL; + int tag_id; + char *tagname = NewAtom_N(char, MAX_TAG_LEN), + attrname[MAX_TAG_LEN]; + char *p, *q; + int i, attr_id, nattr; + + /* Parse tag name */ + q = (*s) + 1; + p = tagname; + if (*q == '/') { + *(p++) = *(q++); + SKIP_BLANKS(q); + } + while (*q && !IS_SPACE(*q) && *q != '>' && + p - tagname < MAX_TAG_LEN - 1) { + *(p++) = tolower(*(q++)); + } + *p = '\0'; + + tag_id = getHash_si(&tagtable, tagname, HTML_UNKNOWN); + + if (tag_id == HTML_UNKNOWN || + (!internal && TagMAP[tag_id].flag & TFLG_INT)) + goto skip_parse_tagarg; + + tag = New(struct parsed_tag); + bzero(tag, sizeof(struct parsed_tag)); + tag->tagid = tag_id; + tag->tagname = tagname; + + if ((nattr = TagMAP[tag_id].max_attribute) > 0) { + tag->attrid = NewAtom_N(unsigned char, nattr); + tag->value = New_N(char *, nattr); + tag->map = NewAtom_N(unsigned char, MAX_TAGATTR); + memset(tag->map, MAX_TAGATTR, MAX_TAGATTR); + memset(tag->attrid, ATTR_UNKNOWN, nattr); + for (i = 0; i < nattr; i++) + tag->map[TagMAP[tag_id].accept_attribute[i]] = i; + } + + /* Parse tag arguments */ + SKIP_BLANKS(q); + while (1) { + Str value = NULL; + if (*q == '>' || *q == '\0') + goto done_parse_tag; + p = attrname; + while (*q && *q != '=' && !IS_SPACE(*q) && + *q != '>' && p - attrname < MAX_TAG_LEN - 1) { + *(p++) = tolower(*(q++)); + } + if (q == p) { + q++; + continue; + } + *p = '\0'; + SKIP_BLANKS(q); + if (*q == '=') { + /* get value */ + value = Strnew(); + q++; + SKIP_BLANKS(q); + if (*q == '"') { + q++; + while (*q && *q != '"') { + if (*q != '\n') + Strcat_char(value, *q); + if (!tag->need_reconstruct && htmlquote_char(*q)) + tag->need_reconstruct = TRUE; + q++; + } + if (*q == '"') + q++; + } + else if (*q == '\'') { + q++; + while (*q && *q != '\'') { + if (*q != '\n') + Strcat_char(value, *q); + if (!tag->need_reconstruct && htmlquote_char(*q)) + tag->need_reconstruct = TRUE; + q++; + } + if (*q == '\'') + q++; + } + else if (*q) { + while (*q && !IS_SPACE(*q) && *q != '>') + Strcat_char(value, *q++); + } + } + for (i = 0; i < nattr; i++) { + if ((tag)->attrid[i] == ATTR_UNKNOWN && + strcmp(AttrMAP[TagMAP[tag_id].accept_attribute[i]].name, + attrname) == 0) { + attr_id = TagMAP[tag_id].accept_attribute[i]; + break; + } + } + if (i != nattr) { + if (!internal && + ((AttrMAP[attr_id].flag & AFLG_INT) || + (value && AttrMAP[attr_id].vtype == VTYPE_METHOD && + !strcasecmp(value->ptr, "internal")))) { + tag->need_reconstruct = TRUE; + continue; + } + tag->attrid[i] = attr_id; + if (value) + tag->value[i] = cleanup_str(value->ptr); + else + tag->value[i] = NULL; + } + } + + skip_parse_tagarg: + while (*q != '>' && *q) + q++; + done_parse_tag: + if (*q == '>') + q++; + *s = q; + return tag; +} + +int +parsedtag_set_value(struct parsed_tag *tag, int id, char *value) +{ + int i; + + if (!parsedtag_accepts(tag, id)) + return 0; + + i = tag->map[id]; + tag->attrid[i] = id; + if (value) + tag->value[i] = allocStr(value, 0); + else + tag->value[i] = NULL; + tag->need_reconstruct = TRUE; + return 1; +} + +int +parsedtag_get_value(struct parsed_tag *tag, int id, void *value) +{ + int i; + if (!parsedtag_exists(tag, id) || + !tag->value[i = tag->map[id]]) + return 0; + return toValFunc[AttrMAP[id].vtype](tag->value[i], value); +} + +Str +parsedtag2str(struct parsed_tag *tag) +{ + int i; + int tag_id = tag->tagid; + int nattr = TagMAP[tag_id].max_attribute; + Str tagstr = Strnew(); + Strcat_char(tagstr, '<'); + Strcat_charp(tagstr, tag->tagname); + for (i = 0; i < nattr; i++) { + if (tag->attrid[i] != ATTR_UNKNOWN) { + Strcat_char(tagstr, ' '); + Strcat_charp(tagstr, AttrMAP[tag->attrid[i]].name); + if (tag->value[i]) + Strcat(tagstr, + Sprintf("=\"%s\"", htmlquote_str(tag->value[i]))); + } + } + Strcat_char(tagstr, '>'); + return tagstr; +} diff --git a/parsetagx.h b/parsetagx.h new file mode 100644 index 0000000..6014784 --- /dev/null +++ b/parsetagx.h @@ -0,0 +1,28 @@ +#ifndef PARSETAGX_H +#define PARSETAGX_H + +#include "html.h" +#include "Str.h" + +/* Parsed Tag structure */ + +struct parsed_tag { + unsigned char tagid; + char *tagname; + unsigned char *attrid; + char **value; + unsigned char *map; + char need_reconstruct; +}; + +#define parsedtag_accepts(tag, id) ((tag)->map&&(tag)->map[id]!=MAX_TAGATTR) +#define parsedtag_exists(tag, id) (parsedtag_accepts(tag,id)&&((tag)->attrid[(tag)->map[id]]!=ATTR_UNKNOWN)) +#define parsedtag_delete(tag, id) (parsedtag_accepts(tag,id)&&((tag)->attrid[(tag)->map[id]]=ATTR_UNKNOWN)) +#define parsedtag_need_reconstruct(tag) ((tag)->need_reconstruct) +#define parsedtag_attname(tag, i) (AttrMAP[(tag)->attrid[i]].name) + +extern struct parsed_tag *parse_tag(char **s, int internal); +extern int parsedtag_get_value(struct parsed_tag *tag, int id, void *value); +extern int parsedtag_set_value(struct parsed_tag *tag, int id, char *value); +extern Str parsedtag2str(struct parsed_tag *tag); +#endif @@ -0,0 +1,514 @@ +/* $Id: proto.h,v 1.1 2001/11/08 05:15:28 a-ito Exp $ */ +/* + * This file was automatically generated by version 1.7 of cextract. + * Manual editing not recommended. + * + * Created: Wed Feb 10 12:47:03 1999 + */ +extern int main(int argc, char **argv, char **envp); +extern void nulcmd(void); +extern void pushEvent(int event, void *user_data); +extern MySignalHandler intTrap(SIGNAL_ARG); +extern MySignalHandler resize_hook(SIGNAL_ARG); +extern void pgFore(void); +extern void pgBack(void); +extern void lup1(void); +extern void ldown1(void); +extern void ctrCsrV(void); +extern void ctrCsrH(void); +extern void rdrwSc(void); +extern void srchfor(void); +extern void srchbak(void); +extern void srchnxt(void); +extern void srchprv(void); +extern void shiftl(void); +extern void shiftr(void); +extern void col1R(void); +extern void col1L(void); +extern void pipesh(void); +extern void readsh(void); +extern void execsh(void); +extern void ldfile(void); +extern void ldhelp(void); +extern void movL(void); +extern void movD(void); +extern void movU(void); +extern void movR(void); +extern void movLW(void); +extern void movRW(void); +extern void qquitfm(void); +extern void quitfm(void); +extern void selBuf(void); +extern void susp(void); +extern void goLine(void); +extern void goLineF(void); +extern void goLineL(void); +extern void linbeg(void); +extern void linend(void); +extern void editBf(void); +extern void editScr(void); +extern void followA(void); +extern void bufferA(void); +extern void followI(void); +extern void followForm(void); +extern void topA(void); +extern void lastA(void); +extern void onA(void); + +extern void nextA(void); +extern void prevA(void); +extern void backBf(void); +extern void deletePrevBuf(void); +extern void goURL(void); +extern void ldBmark(void); +extern void adBmark(void); +extern void ldOpt(void); +extern void pginfo(void); +extern void msgs(void); +extern void svA(void); +extern void svI(void); +extern void svBuf(void); +extern void svSrc(void); +extern void peekURL(void); +extern void peekIMG(void); +extern void curURL(void); +extern void vwSrc(void); +extern void reload(void); +extern void chkURL(void); +#ifdef USE_NNTP +extern void chkNMID(void); +#else +#define chkNMID nulcmd +#endif +extern void rFrame(void); +extern void extbrz(void); +extern void linkbrz(void); +extern void curlno(void); +extern int currentLn(Buffer * buf); +extern void tmpClearBuffer(Buffer * buf); +extern char *filename_extension(char *patch, int is_url); +extern void examineFile(char *path, URLFile *uf); +extern int dir_exist(char *path); +extern Str convertLine(URLFile * uf, Str line, char *code, int mode); +extern Buffer *loadFile(char *path); +extern Buffer *loadGeneralFile(char *path, ParsedURL * current, char *referer, int flag, FormList * request); +extern int is_boundary(int, int); +extern int is_blank_line(char *line, int indent); +extern void push_render_image(Str str, int width, struct html_feed_environ *h_env); +extern void flushline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent, int force, int width); +extern void do_blankline(struct html_feed_environ *h_env, struct readbuffer *obuf, int indent, int indent_incr, int width); +extern void purgeline(struct html_feed_environ *h_env); +extern void save_fonteffect(struct html_feed_environ *h_env, + struct readbuffer *obuf); +extern void restore_fonteffect(struct html_feed_environ *h_env, + struct readbuffer *obuf); +extern Str process_img(struct parsed_tag *tag); +extern Str process_anchor(struct parsed_tag *tag, char *tagbuf); +extern Str process_input(struct parsed_tag *tag); +extern void process_select(struct parsed_tag *tag); +extern Str process_n_select(void); +extern void feed_select(char *str); +extern void process_option(void); +extern Str process_textarea(struct parsed_tag *tag, int width); +extern Str process_n_textarea(void); +extern void feed_textarea(char *str); +#ifdef NEW_FORM +extern Str process_form(struct parsed_tag *tag); +extern Str process_n_form(void); +#endif /* NEW_FORM */ +extern int HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env); +extern void HTMLlineproc2(Buffer * buf, TextLineList * tl); +extern void HTMLlineproc0(char *istr, struct html_feed_environ *h_env, int internal); +#define HTMLlineproc1(x,y) HTMLlineproc0(x,y,TRUE) +extern Buffer *loadHTMLBuffer(URLFile * f, Buffer * newBuf); +extern void showProgress(int *linelen, int *trbyte); +extern void init_henv(struct html_feed_environ *, struct readbuffer *, + struct environment *, int, TextLineList *, int, int); +extern void completeHTMLstream(struct html_feed_environ *, struct readbuffer *); +extern void loadHTMLstream(URLFile * f, Buffer * newBuf, FILE * src, int internal); +extern Buffer *loadHTMLString(Str page); +#ifdef USE_GOPHER +extern Buffer *loadGopherDir(URLFile * uf, Buffer * newBuf); +#endif /* USE_GOPHER */ +extern Buffer *loadBuffer(URLFile * uf, Buffer * newBuf); +extern void saveBuffer(Buffer * buf, FILE * f); +extern void saveBufferDelNum(Buffer * buf, FILE * f, int del); +extern Buffer *getshell(char *cmd); +extern Buffer *getpipe(char *cmd); +extern Buffer *openPagerBuffer(InputStream stream, Buffer * buf); +extern Buffer *openGeneralPagerBuffer(InputStream stream); +extern Line *getNextPage(Buffer * buf, int plen); +extern int save2tmp(URLFile uf, char *tmpf); +extern int doExternal(URLFile uf, char *path, char *type, Buffer **bufp, Buffer *defaultbuf); +extern void doFileCopy(char *tmpf, char *defstr); +extern void doFileMove(char *tmpf, char *defstr); +extern void doFileSave(URLFile uf, char *defstr); +extern int checkCopyFile(char *path1, char *path2); +extern int checkSaveFile(InputStream stream, char *path); +extern int checkOverWrite(char *path); +extern Buffer *newBuffer(int width); +extern Buffer *nullBuffer(void); +extern void clearBuffer(Buffer * buf); +extern void discardBuffer(Buffer * buf); +extern Buffer *namedBuffer(Buffer * first, char *name); +extern Buffer *deleteBuffer(Buffer * first, Buffer * delbuf); +extern Buffer *replaceBuffer(Buffer * first, Buffer * delbuf, Buffer * newbuf); +extern Buffer *nthBuffer(Buffer * firstbuf, int n); +extern void gotoRealLine(Buffer * buf, int n); +extern void gotoLine(Buffer * buf, int n); +extern Buffer *selectBuffer(Buffer * firstbuf, Buffer * currentbuf, char *selectchar); +extern void reshapeBuffer(Buffer * buf); +extern void copyBuffer(Buffer * a, Buffer * b); +extern Buffer *prevBuffer(Buffer * first, Buffer * buf); +extern int writeBufferCache(Buffer *buf); +extern int readBufferCache(Buffer *buf); +extern void fmTerm(void); +extern void fmInit(void); +extern void deleteFiles(void); +extern void displayBuffer(Buffer * buf, int mode); +extern void redrawBuffer(Buffer * buf); +extern void redrawNLine(Buffer * buf, int n); +extern Line *redrawLine(Buffer * buf, Line * l, int i); +extern int redrawLineRegion(Buffer * buf, Line * l, int i, int bpos, int epos); +extern void do_effects(Lineprop m); +#ifdef ANSI_COLOR +extern void do_color(Linecolor c); +#endif +extern void addChar(char c, Lineprop mode); +extern GeneralList *message_list; +extern void record_err_message(char *s); +extern Buffer *message_list_panel(void); +extern void message(char *s, int return_x, int return_y); +#define disp_err_message(s, f) (record_err_message((s)), disp_message((s), (f))) +extern void disp_message_nsec(char *s, int redraw_current, int sec, int purge, int mouse); +extern void disp_message(char *s, int redraw_current); +#ifdef MOUSE +extern void disp_message_nomouse(char *s, int redraw_current); +#else +#define disp_message_nomouse disp_message +#endif +extern void cursorUp(Buffer * buf); +extern void cursorDown(Buffer * buf); +extern void cursorUpDown(Buffer * buf, int n); +extern void cursorRight(Buffer * buf); +extern void cursorLeft(Buffer * buf); +extern void cursorHome(Buffer * buf); +extern void arrangeCursor(Buffer * buf); +extern void arrangeLine(Buffer * buf); +extern void cursorXY(Buffer * buf, int x, int y); +extern int arg_is(char *str, char *tag); +extern int columnSkip(Buffer * buf, int offset); +extern int columnPos(Line * line, int column); +extern Line *lineSkip(Buffer * buf, Line * line, int offset, int last); +extern Line *currentLineSkip(Buffer * buf, Line * line, int offset, int last); +extern int gethtmlcmd(char **s, int *status); +extern char *getAnchor(char *arg, char **arg_return); +extern Str checkType(Str s, Lineprop * oprop, +#ifdef ANSI_COLOR + Linecolor * ocolor, int * check_color, +#endif + int len); +extern int calcPosition(char *l, Lineprop *pr, int len, int pos, int bpos, int mode); +extern char *lastFileName(char *path); +extern char *mybasename(char *s); +extern char *mydirname(char *s); +extern int next_status(char c, int *status); +extern int read_token(Str buf, char **instr, int *status, int pre, int append); +extern Str correct_irrtag(int status); +extern int forwardSearch(Buffer * buf, char *str); +extern int backwardSearch(Buffer * buf, char *str); +extern void pcmap ( void ); +extern void escmap(void); +extern void escbmap(void); +extern void escdmap(char c); +extern char *inputLineHist(char *prompt, char *def_str, int flag, Hist * hist); +#ifdef USE_HISTORY +extern Buffer *historyBuffer(Hist *hist); +extern void loadHistory(Hist *hist); +extern void saveHistory(Hist *hist, size_t size); +extern void ldHist(void); +#else /* not USE_HISTORY */ +#define ldHist nulcmd +#endif /* not USE_HISTORY */ +extern double log_like(int x); +extern struct table *newTable(void); +extern void pushdata(struct table *t, int row, int col, char *data); +extern int visible_length(char *str); +extern void align(TextLine *lbuf, int width, int mode); +extern void print_item(struct table *t, int row, int col, int width, Str buf); +extern void print_sep(struct table *t, int row, int type, int maxcol, Str buf); +extern void do_refill(struct table *tbl, int row, int col, int maxlimit); +extern void renderTable(struct table *t, int max_width, struct html_feed_environ *h_env); +extern struct table *begin_table(int border, int spacing, int padding, int vspace); +extern void end_table(struct table * tbl); +extern void check_rowcol(struct table *tbl, struct table_mode *mode); +extern int minimum_length(char *line); +extern int feed_table(struct table *tbl, char *line, struct table_mode *mode, int width, int internal); +extern void feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width); +extern void pushTable(struct table *, struct table *); +extern struct form_list *newFormList(char *action, char *method, char *charset, char *enctype, char *target, struct form_list *_next); +extern struct form_item_list *formList_addInput(struct form_list *fl, struct parsed_tag *tag); +extern char *form2str(FormItemList * fi); +extern int formtype(char *typestr); +extern void form_recheck_radio(FormItemList * fi, void *data, void (*update_hook) (FormItemList *, void *)); +extern void formResetBuffer(Buffer * buf, AnchorList * formitem); +extern void formUpdateBuffer(Anchor * a, Buffer * buf, FormItemList * form); +extern Str textfieldrep(Str s, int width); +extern void input_textarea(FormItemList * fi); +extern void do_internal(char *action, char *data); +extern void form_write_data(FILE * f, char *boundary, char *name, char *value); +extern void form_write_form_file(FILE * f, char *boundary, char *name, char *file); +extern void follow_map(struct parsed_tagarg *arg); +#ifdef MENU_MAP +extern char *follow_map_menu(Buffer * buf, struct parsed_tagarg *arg, int x, int y); +#else +extern Buffer *follow_map_panel(Buffer * buf, struct parsed_tagarg *arg); +#endif +extern Buffer *page_info_panel(Buffer * buf); +extern struct frame_body *newFrame(struct parsed_tag *tag, ParsedURL * baseURL); +extern struct frameset *newFrameSet(struct parsed_tag *tag); +extern void addFrameSetElement(struct frameset *f, union frameset_element element); +extern void deleteFrame(struct frame_body *b); +extern void deleteFrameSet(struct frameset *f); +extern void deleteFrameSetElement(union frameset_element e); +extern struct frameset *copyFrameSet(struct frameset *of); +extern void pushFrameTree(struct frameset_queue **fqpp, struct frameset *fs, long linenumber, short pos); +extern struct frameset *popFrameTree(struct frameset_queue **fqpp, long *linenumber, short *pos); +extern void resetFrameElement(union frameset_element *f_element, Buffer * buf, char *referer, FormList * request); +extern Buffer *renderFrame(Buffer * Cbuf, int force_reload); +extern union frameset_element *search_frame(struct frameset *fset, char *name); +extern int set_tty(void); +extern void set_cc(int spec, int val); +extern void close_tty(void); +extern void reset_tty(void); +extern MySignalHandler reset_exit(SIGNAL_ARG); +extern MySignalHandler error_dump(SIGNAL_ARG); +extern void set_int(void); +extern void getTCstr(void); +extern void setlinescols(void); +extern void setupscreen(void); +extern int initscr(void); +extern int write1(char c); +extern void endline(void); +extern void switch_ascii(FILE * f); +extern void switch_wchar(FILE * f); +extern void putchars(unsigned char c1, unsigned char c2, FILE * f); +extern void move(int line, int column); +extern void addch(char c); +extern void wrap(void); +extern void touch_line(void); +extern void standout(void); +extern void standend(void); +extern void bold(void); +extern void boldend(void); +extern void underline(void); +extern void underlineend(void); +extern void graphstart(void); +extern void graphend(void); +extern int graph_ok(void); +#ifdef COLOR +extern void setfcolor(int color); +#ifdef BG_COLOR +extern void setbcolor(int color); +#endif /* BG_COLOR */ +#endif /* COLOR */ +extern void refresh(void); +extern void clear(void); +extern void scroll(int); +extern void rscroll(int); +extern void need_clrtoeol(void); +extern void clrtoeol(void); +extern void clrtoeolx(void); +extern void clrtobot(void); +extern void clrtobotx(void); +extern void no_clrtoeol(void); +extern void addstr(char *s); +extern void addnstr(char *s, int n); +extern void addnstr_sup(char *s, int n); +extern void crmode(void); +extern void nocrmode(void); +extern void term_echo(void); +extern void term_noecho(void); +extern void term_raw(void); +extern void term_cooked(void); +extern void term_cbreak(void); +extern void flush_tty(void); +extern void toggle_stand(void); +extern char getch(void); +extern void bell(void); +extern void sleep_till_anykey(int sec, int purge); +#ifdef JP_CHARSET +extern char *GetSICode(char key); +extern char *GetSOCode(char key); +extern Str conv_str(Str is, char fc, char tc); +extern Str conv(char *is, char fc, char tc); +extern char checkShiftCode(Str buf, unsigned char hint); +extern char str_to_code(char *str); +extern char *code_to_str(char code); +extern void put_sjis(Str os, unsigned char ub, unsigned char lb); +#endif /* JP_CHARSET */ +extern ParsedURL *baseURL(Buffer * buf); +extern int openSocket(char *hostname, char *remoteport_name, unsigned short remoteport_num); +extern void parseURL(char *url, ParsedURL * p_url, ParsedURL * current); +extern void copyParsedURL(ParsedURL * p, ParsedURL * q); +extern void parseURL2(char *url, ParsedURL * pu, ParsedURL * current); +extern Str parsedURL2Str(ParsedURL * pu); +extern int getURLScheme(char **url); +extern void init_stream(URLFile *uf, int scheme, InputStream stream); +extern URLFile openURL(char *url, ParsedURL * pu, ParsedURL * current, URLOption * option, FormList * request, TextList * extra_header, URLFile * ouf, unsigned char *status); +extern int mailcapMatch(struct mailcap *mcap, char *type); +extern struct mailcap *loadMailcap(char *filename); +extern struct mailcap *searchMailcap(struct mailcap *table, char *type); +extern void initMailcap(); +extern struct mailcap *searchExtViewer(char *type); +extern Str unquote_mailcap(char *qstr, char *type, char *name, int *stat); +extern char *guessContentTypeFromTable(struct table2 *table, char *filename); +extern char *guessContentType(char *filename); +extern TextList *make_domain_list(char *domain_list); +extern int check_no_proxy(char *domain); +extern FILE *openFTP(ParsedURL * pu); +extern void closeFTP(FILE * f); +extern int Ftpfclose(FILE * f); +extern AnchorList * putAnchor(AnchorList * al, char *url, char *target, Anchor ** anchor_return, char *referer, int line, int pos); +extern Anchor *registerHref(Buffer * buf, char *url, char *target, char *referer, int line, int pos); +extern Anchor *registerName(Buffer * buf, char *url, int line, int pos); +extern Anchor *registerImg(Buffer * buf, char *url, int line, int pos); +extern Anchor *registerForm(Buffer * buf, FormList * flist, struct parsed_tag *tag, int line, int pos); +extern int onAnchor(Anchor * a, int line, int pos); +extern Anchor *retrieveAnchor(AnchorList * al, int line, int pos); +extern Anchor *retrieveCurrentAnchor(Buffer * buf); +extern Anchor *retrieveCurrentImg(Buffer * buf); +extern Anchor *retrieveCurrentForm(Buffer * buf); +extern Anchor *searchAnchor(AnchorList * al, char *str); +extern Anchor *searchURLLabel(Buffer * buf, char *url); +extern char *reAnchor(Buffer * buf, char *re); +#ifdef USE_NNTP +extern char *reAnchorNews(Buffer * buf, char *re); +#endif /* USE_NNTP */ +extern Anchor *closest_next_anchor(AnchorList * a, Anchor * an, int x, int y); +extern Anchor *closest_prev_anchor(AnchorList * a, Anchor * an, int x, int y); +extern HmarkerList *putHmarker(HmarkerList * ml, int line, int pos, int seq); +extern Str decodeB(char **ww); +extern Str decodeQ(char **ww); +extern Str decodeQP(char **ww); +extern Str decodeWord(char **ow); +extern Str decodeMIME(char *orgstr); +extern Str encodeB(char *a); +extern int set_param_option(char *option); +extern void create_option_search_table(); +extern void init_rc(char *config_file); +extern Buffer *load_option_panel(void); +extern void panel_set_option(struct parsed_tagarg *); +extern char *rcFile(char *base); +extern char *libFile(char *base); +extern char *helpFile(char *base); +extern void setLocalCookie(void); +extern Buffer *dirBuffer(char *dirname); +extern FILE *localcgi_post(char *, FormList *, char*); +extern FILE *localcgi_get(char *, char *, char*); +extern Str find_auth_cookie(char *host, char *realm); +extern void add_auth_cookie(char *host, char *realm, Str cookie); +extern char *last_modified(Buffer * buf); +extern Str romanNumeral(int n); +extern Str romanAlphabet(int n); +extern Str quoteShell(char *command); +extern void mySystem(char *command, int background); +extern char *expandName(char *name); +extern Str tmpfname(int type, char *ext); +#ifdef USE_COOKIE +extern time_t mymktime(char *timestr); +extern char *FQDN(char *host); +extern Str find_cookie(ParsedURL * pu); +extern int add_cookie(ParsedURL * pu, Str name, Str value, time_t expires, + Str domain, Str path, int flag, Str comment, int version, + Str port, Str commentURL); +extern void save_cookies(void); +extern void load_cookies(void); +extern void initCookie(void); +extern void cooLst(void); +extern Buffer *cookie_list_panel(void); +extern void set_cookie_flag(struct parsed_tagarg *arg); +extern int check_cookie_accept_domain(char *domain); +#else /* not USE_COOKIE */ +#define cooLst nulcmd +#endif /* not USE_COOKIE */ + +#ifdef USE_MARK +extern void _mark(void); +extern void nextMk(void); +extern void prevMk(void); +extern void reMark(void); +#else /* not USE_MARK */ +#define _mark nulcmd +#define nextMk nulcmd +#define prevMk nulcmd +#define reMark nulcmd +#endif /* not USE_MARK */ + +#ifdef MOUSE +extern void mouse(void); +extern void mouse_init(void); +extern void mouse_end(void); +extern void mouse_active(void); +extern void mouse_inactive(void); +extern void msToggle(void); +#else /* not MOUSE */ +#define mouse nulcmd +#define msToggle nulcmd +#endif /* not MOUSE */ + +extern char *searchKeyData(void); + +extern void initKeymap(void); +extern int countFuncList(FuncList * list); +extern int getFuncList(char *id, FuncList * list, int nlist); +extern int getKey(char *s); +extern void addKeyList(KeyList *list, int key, char *data); +extern KeyListItem *searchKeyList(KeyList *list, int key); +extern char *getWord(char **str); +extern char *getQWord(char **str); + +#ifdef MENU +extern void new_menu(Menu * menu, MenuItem * item); +extern void geom_menu(Menu * menu, int x, int y, int select); +extern void draw_all_menu(Menu * menu); +extern void draw_menu(Menu * menu); +extern void draw_menu_item(Menu * menu, int select); +extern int select_menu(Menu * menu, int select); +extern void goto_menu(Menu * menu, int select, int down); +extern void up_menu(Menu * menu, int n); +extern void down_menu(Menu * menu, int n); +extern int action_menu(Menu * menu); +extern void popup_menu(Menu * parent, Menu * menu); +extern void guess_menu_xy(Menu * menu, int width, int *x, int *y); +extern void new_option_menu(Menu * menu, char **label, int *variable, void (*func) ()); + +extern int setMenuItem(MenuItem * item, char *type, char *line); +extern int addMenuList(MenuList ** list, char *id); +extern int getMenuN(MenuList * list, char *id); + +extern void popupMenu(int x, int y, Menu *menu); +extern void mainMenu(int x, int y); +extern void mainMn(void); +extern void optionMenu(int x, int y, char **label, int *variable, int initial, void (*func) ()); +extern void initMenu(void); +#else /* not MENU */ +#define mainMn nulcmd +#endif /* not MENU */ + +#ifdef DICT +extern void dictword(void); +extern void dictwordat(void); +#else /* not DICT */ +#define dictword nulcmd +#define dictwordat nulcmd +#endif /* not DICT */ + +extern void reloadBuffer(Buffer * buf); + +extern char *guess_save_name(char *file); + +extern void wrapToggle(void); +extern void saveBufferInfo(void); +extern char*get_os2_dft(const char*,char*); +#include "indep.h" @@ -0,0 +1,1256 @@ + +/* + * 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> + +struct param_ptr { + char *name; + int type; + int inputtype; + void *varptr; + char *comment; + struct sel_c *select; +}; + +struct param_section { + char *name; + struct param_ptr *params; +}; + +struct rc_search_table { + struct param_ptr *param; + short uniq_pos; +}; + +static struct rc_search_table *RC_search_table; +static int RC_table_size; + +static int rc_initialized = 0; + +#define P_INT 0 +#define P_SHORT 1 +#define P_CHARINT 2 +#define P_CHAR 3 +#define P_STRING 4 +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) +#define P_SSLPATH 5 +#endif +#ifdef COLOR +#define P_COLOR 6 +#endif +#ifdef JP_CHARSET +#define P_CODE 7 +#endif +#define P_PIXELS 8 + +#if LANG == JA +#define CMT_HELPER "�����ӥ塼�����Խ�" +#define CMT_TABSTOP "������" +#define CMT_PIXEL_PER_CHAR "ʸ���� (4.0...32.0)" +#define CMT_PAGERLINE "�ڡ�����Ȥ������Ѥ���������¸�����Կ�" +#define CMT_HISTSIZE "�ݻ�����URL����ο�" +#define CMT_SAVEHIST "URL�������¸" +#define CMT_KANJICODE "ɽ���Ѵ���������" +#define CMT_FRAME "�ե졼��μ�ưɽ��" +#define CMT_TSELF "target��̤����ξ���_self����Ѥ���" +#define CMT_DISPLINK "�����μ�ưɽ��" +#define CMT_MULTICOL "�ե�����̾�Υޥ�������ɽ��" +#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_ACTIVE_STYLE "��������Ƥ����ο�����ꤹ��" +#define CMT_C_COLOR "��������Ƥ����ο�" +#define CMT_VISITED_ANCHOR "ˬ�줿���Ȥ������Ͽ����Ѥ���" +#define CMT_V_COLOR "ˬ�줿���Ȥ������ο�" +#define CMT_HTTP_PROXY "HTTP�ץ�����(URL������)" +#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_DROOT "/ ��ɽ�����ǥ��쥯�ȥ�(document root)" +#define CMT_PDROOT "/~user ��ɽ�����ǥ��쥯�ȥ�" +#define CMT_CGIBIN "/cgi-bin ��ɽ�����ǥ��쥯�ȥ�" +#define CMT_CONFIRM_QQ "q �Ǥν�λ���˳�ǧ����" +#define CMT_SHOW_NUM "���ֹ��ɽ������" +#define CMT_MIMETYPES "���Ѥ���mime.types" +#define CMT_MAILCAP "���Ѥ���mailcap" +#define CMT_EDITOR "���Ѥ��륨�ǥ���" +#define CMT_MAILER "���Ѥ�����" +#define CMT_EXTBRZ "�����֥饦��" +#define CMT_EXTBRZ2 "�����֥饦������2" +#define CMT_EXTBRZ3 "�����֥饦������3" +#define CMT_FTPPASS "FTP�Υѥ����(���̤ϼ�ʬ��mail address��Ȥ�)" +#define CMT_USERAGENT "User-Agent" +#define CMT_ACCEPTLANG "�����Ĥ������(Accept-Language:)" +#define CMT_DOCUMENTCODE "ʸ���ʸ��������" +#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 "�ǥ��쥯�ȥ�ꥹ���ѥ��ޥ��" +#define CMT_IGNORE_NULL_IMG_ALT "����IMG ALT°���λ��˥��̾��ɽ������" +#define CMT_IFILE "�ƥǥ��쥯�ȥ�Υ���ǥå����ե�����" +#define CMT_RETRY_HTTP "URL�˼�ưŪ�� http:// ���䤦" +#ifdef MOUSE +#define CMT_MOUSE "�ޥ�����Ȥ�" +#define CMT_REVERSE_MOUSE "�ޥ����Υɥ�å�ư���դˤ���" +#endif /* MOUSE */ +#define CMT_CLEAR_BUF "ɽ������Ƥ��ʤ��Хåե��Υ����������" +#define CMT_NOSENDREFERER "Referer: ������ʤ��褦�ˤ���" +#define CMT_IGNORE_CASE "������������ʸ����ʸ���ζ��̤ʤ�" +#define CMT_USE_LESSOPEN "LESSOPEN�����" +#if defined(USE_SSL) && defined(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 /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ +#ifdef USE_SSL +#define CMT_SSL_FORBID_METHOD "�Ȥ�ʤ�SSL��åɤΥꥹ��(2: SSLv2, 3: SSLv3, t:TLSv1)" +#endif +#ifdef USE_COOKIE +#define CMT_USECOOKIE "���å�������Ѥ���" +#define CMT_ACCEPTCOOKIE "���å���������դ���" +#define CMT_ACCEPTBADCOOKIE "����Τ��륯�å����Ǥ�����դ���" +#define CMT_COOKIE_REJECT_DOMAINS "���å���������դ��ʤ��ɥᥤ��" +#define CMT_COOKIE_ACCEPT_DOMAINS "���å���������դ���ɥᥤ��" +#endif + + +#else /* LANG != JA */ + + +#define CMT_HELPER "External Viewer Setup" +#define CMT_TABSTOP "Tab width" +#define CMT_PIXEL_PER_CHAR "# of pixels per character (4.0...32.0)" +#define CMT_PAGERLINE "# of reserved line when w3m is used as a pager" +#define CMT_HISTSIZE "# of reserved URL" +#define CMT_SAVEHIST "Save URL history" +/* #define CMT_KANJICODE "Display Kanji Code" */ +#define CMT_FRAME "Automatic rendering of frame" +#define CMT_TSELF "use _self as default target" +#define CMT_DISPLINK "Automatic display of link URL" +#define CMT_MULTICOL "Multi-column output of file names" +#define CMT_COLOR "Display with color" +#define CMT_B_COLOR "Color of normal character" +#define CMT_A_COLOR "Color of anchor" +#define CMT_I_COLOR "Color of image link" +#define CMT_F_COLOR "Color of form" +#define CMT_ACTIVE_STYLE "Use active link color" +#define CMT_C_COLOR "Color of currently active link" +#define CMT_VISITED_ANCHOR "Use visited link color" +#define CMT_V_COLOR "Color of visited link" +#define CMT_BG_COLOR "Color of background" +#define CMT_HTTP_PROXY "URL of HTTP proxy host" +#ifdef USE_GOPHER +#define CMT_GOPHER_PROXY "URL of GOPHER proxy host" +#endif /* USE_GOPHER */ +#define CMT_FTP_PROXY "URL of FTP proxy host" +#define CMT_NO_PROXY "Domains for direct access (no proxy)" +#define CMT_NOPROXY_NETADDR "Check noproxy by network address" +#define CMT_DNS_ORDER "Order of name resolution" +#define CMT_DROOT "Directory corresponds to / (document root)" +#define CMT_PDROOT "Directory corresponds to /~user" +#define CMT_CGIBIN "Directory corresponds to /cgi-bin" +#define CMT_CONFIRM_QQ "Confirm when quitting with q" +#define CMT_SHOW_NUM "Show line number" +#define CMT_MIMETYPES "mime.types files" +#define CMT_MAILCAP "mailcap files" +#define CMT_EDITOR "Editor" +#define CMT_MAILER "Mailer" +#define CMT_EXTBRZ "External Browser" +#define CMT_EXTBRZ2 "Second External Browser" +#define CMT_EXTBRZ3 "Third External Browser" +#define CMT_FTPPASS "Password for FTP(use your mail address)" +#define CMT_USERAGENT "User-Agent" +#define CMT_ACCEPTLANG "Accept-Language" +/* #define CMT_DOCUMENTCODE "Document Charset" */ +#define CMT_WRAP "Wrap search" +#define CMT_VIEW_UNSEENOBJECTS "Display unseenobjects (e.g. bgimage) tag" +#ifdef __EMX__ +#define CMT_BGEXTVIEW "Another session for an external viewer" +#else +#define CMT_BGEXTVIEW "Background an external viewer" +#endif +#define CMT_EXT_DIRLIST "Use external program for directory listing" +#define CMT_DIRLIST_CMD "Directory listing command" +#define CMT_IGNORE_NULL_IMG_ALT "Ignore IMG ALT=\"\" (display link name)" +#define CMT_IFILE "Index file for the directory" +#define CMT_RETRY_HTTP "Prepend http:// to URL automatically" +#ifdef MOUSE +#define CMT_MOUSE "Use mouse" +#define CMT_REVERSE_MOUSE "Reverse mouse dragging action" +#endif /* MOUSE */ +#define CMT_CLEAR_BUF "Free memory of the undisplayed buffers" +#define CMT_NOSENDREFERER "Don't send header `Referer:'" +#define CMT_IGNORE_CASE "Ignore case when search" +#define CMT_USE_LESSOPEN "Use LESSOPEN" +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) +#define CMT_SSL_VERIFY_SERVER "Perform SSL server verification" +#define CMT_SSL_CERT_FILE "PEM encoded certificate file of client" +#define CMT_SSL_KEY_FILE "PEM encoded private key file of client" +#define CMT_SSL_CA_PATH "Path to a directory for PEM encoded certificates of CAs" +#define CMT_SSL_CA_FILE "File consisting of PEM encoded certificates of CAs" +#endif /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ +#ifdef USE_SSL +#define CMT_SSL_FORBID_METHOD "List of forbidden SSL method (2: SSLv2, 3: SSLv3, t:TLSv1)" +#endif +#ifdef USE_COOKIE +#define CMT_USECOOKIE "Use Cookie" +#define CMT_ACCEPTCOOKIE "Accept Cookie" +#define CMT_ACCEPTBADCOOKIE "Invalid Cookie" +#define CMT_COOKIE_REJECT_DOMAINS "Domains from which should reject cookies" +#define CMT_COOKIE_ACCEPT_DOMAINS "Domains from which should accept cookies" +#endif +#endif /* LANG != JA */ + +#define PI_TEXT 0 +#define PI_ONOFF 1 +#define PI_SEL_C 2 + +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} +}; +#endif /* JP_CHARSET */ + +#ifdef 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", "black"}, + {1, "red", "red"}, + {2, "green", "green"}, + {3, "yellow", "yellow"}, + {4, "blue", "blue"}, + {5, "magenta", "magenta"}, + {6, "cyan", "cyan"}, + {7, "white", "white"}, + {8, "terminal", "terminal"}, + {0, NULL, NULL} +#endif /* LANG != JA */ +}; +#endif /* COLOR */ + +#ifdef INET6 +static struct sel_c dnsorders[] = +{ + {0, "0", "unspec"}, + {1, "1", "inet inet6"}, + {2, "2", "inet6 inet"}, + {0, NULL, NULL} +}; +#endif /* INET6 */ + +#ifdef USE_COOKIE +static struct sel_c badcookiestr[] = { + {0, "0", "discard"}, +#if 0 + {1, "1", "accept"}, +#endif + {2, "2", "ask"}, + {0, NULL, NULL} +}; +#endif /* USE_COOKIE */ + +struct param_ptr params1[] = +{ + {"tabstop", P_INT, PI_TEXT, (void *) &Tabstop, CMT_TABSTOP, NULL}, + {"pixel_per_char", P_PIXELS, PI_TEXT, (void *) &pixel_per_char, CMT_PIXEL_PER_CHAR, NULL}, +#ifdef JP_CHARSET + {"kanjicode", P_CODE, PI_SEL_C, (void *) &DisplayCode, CMT_KANJICODE, kcodestr}, +#endif /* JP_CHARSET */ + {"frame", P_CHARINT, PI_ONOFF, (void *) &RenderFrame, CMT_FRAME, NULL}, + {"target_self", P_CHARINT, PI_ONOFF, (void *) &TargetSelf, CMT_TSELF, NULL}, + {"display_link", P_INT, PI_ONOFF, (void *) &displayLink, CMT_DISPLINK, NULL}, + {"ext_dirlist", P_INT, PI_ONOFF, (void *) &UseExternalDirBuffer, CMT_EXT_DIRLIST, NULL}, + {"dirlist_cmd", P_STRING, PI_TEXT, (void *) &DirBufferCommand, CMT_DIRLIST_CMD, NULL}, +{"multicol", P_INT, PI_ONOFF, (void *) &multicolList, CMT_MULTICOL, NULL}, + {"ignore_null_img_alt", P_INT, PI_ONOFF, (void *) &ignore_null_img_alt, CMT_IGNORE_NULL_IMG_ALT, NULL}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; + +#ifdef COLOR +struct param_ptr params2[] = +{ + {"color", P_INT, PI_ONOFF, (void *) &useColor, CMT_COLOR, NULL}, + {"basic_color", P_COLOR, PI_SEL_C, (void *) &basic_color, CMT_B_COLOR, colorstr}, + {"anchor_color", P_COLOR, PI_SEL_C, (void *) &anchor_color, CMT_A_COLOR, colorstr}, + {"image_color", P_COLOR, PI_SEL_C, (void *) &image_color, CMT_I_COLOR, colorstr}, + {"form_color", P_COLOR, PI_SEL_C, (void *) &form_color, CMT_F_COLOR, colorstr}, +#ifdef BG_COLOR + {"bg_color", P_COLOR, PI_SEL_C, (void *) &bg_color, CMT_BG_COLOR, colorstr}, +#endif /* BG_COLOR */ + {"active_style", P_INT, PI_ONOFF, (void *) &useActiveColor, CMT_ACTIVE_STYLE, NULL}, + {"active_color", P_COLOR, PI_SEL_C, (void *) &active_color, CMT_C_COLOR, colorstr}, + {"visited_anchor", P_INT, PI_ONOFF, (void *) &useVisitedColor, CMT_VISITED_ANCHOR, NULL}, + {"visited_color", P_COLOR, PI_SEL_C, (void *) &visited_color, CMT_V_COLOR, colorstr}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; +#endif /* COLOR */ + + +struct param_ptr params3[] = +{ + {"pagerline", P_INT, PI_TEXT, (void *) &PagerMax, CMT_PAGERLINE, NULL}, +#ifdef USE_HISTORY + {"history", P_INT, PI_TEXT, (void *) &URLHistSize, CMT_HISTSIZE, NULL}, +{"save_hist", P_INT, PI_ONOFF, (void *) &SaveURLHist, CMT_SAVEHIST, NULL}, +#endif /* USE_HISTORY */ + {"confirm_qq", P_INT, PI_ONOFF, (void *) &confirm_on_quit, CMT_CONFIRM_QQ, NULL}, +{"show_lnum", P_INT, PI_ONOFF, (void *) &showLineNum, CMT_SHOW_NUM, NULL}, +{"ftppasswd", P_STRING, PI_TEXT, (void *) &ftppasswd, CMT_FTPPASS, NULL}, + {"user_agent", P_STRING, PI_TEXT, (void *) &UserAgent, CMT_USERAGENT, NULL}, + {"no_referer", P_INT, PI_ONOFF, (void *) &NoSendReferer, CMT_NOSENDREFERER, NULL}, + {"accept_language", P_STRING, PI_TEXT, (void *) &AcceptLang, CMT_ACCEPTLANG, NULL}, +#ifdef JP_CHARSET + {"document_code", P_CODE, PI_SEL_C, (void *) &DocumentCode, CMT_DOCUMENTCODE, dcodestr}, +#endif + {"wrap_search", P_INT, PI_ONOFF, (void *) &WrapDefault, CMT_WRAP, NULL}, + {"ignorecase_search", P_INT, PI_ONOFF, (void *) &IgnoreCase, CMT_IGNORE_CASE, NULL}, +#ifdef VIEW_UNSEENOBJECTS + {"view_unseenobject", P_INT, PI_ONOFF, (void *) &view_unseenobject, CMT_VIEW_UNSEENOBJECTS, NULL}, +#endif /* VIEW_UNSEENOBJECTS */ +#ifdef MOUSE + {"use_mouse", P_INT, PI_ONOFF, (void *) &use_mouse, CMT_MOUSE, NULL}, + {"reverse_mouse", P_INT, PI_ONOFF, (void *) &reverse_mouse, CMT_REVERSE_MOUSE, NULL}, +#endif /* MOUSE */ + {"retry_http", P_INT, PI_ONOFF, (void *) &retryAsHttp, CMT_RETRY_HTTP, NULL}, + {"clear_buffer", P_INT, PI_ONOFF, (void *) &clear_buffer, CMT_CLEAR_BUF, NULL}, +#ifdef USE_SSL + {"ssl_forbid_method", P_STRING, PI_TEXT, (void *) &ssl_forbid_method, CMT_SSL_FORBID_METHOD, NULL}, +#endif /* USE_SSL */ + {NULL, 0, 0, NULL, NULL, NULL}, +}; + +struct param_ptr params4[] = +{ + {"http_proxy", P_STRING, PI_TEXT, (void *) &HTTP_proxy, CMT_HTTP_PROXY, NULL}, +#ifdef USE_GOPHER + {"gopher_proxy", P_STRING, PI_TEXT, (void *) &GOPHER_proxy, CMT_GOPHER_PROXY, NULL}, +#endif /* USE_GOPHER */ + {"ftp_proxy", P_STRING, PI_TEXT, (void *) &FTP_proxy, CMT_FTP_PROXY, NULL}, + {"no_proxy", P_STRING, PI_TEXT, (void *) &NO_proxy, CMT_NO_PROXY, NULL}, + {"noproxy_netaddr", P_INT, PI_ONOFF, (void *) &NOproxy_netaddr, CMT_NOPROXY_NETADDR, NULL}, +#ifdef INET6 + {"dns_order", P_INT, PI_SEL_C, (void *) &DNS_order, CMT_DNS_ORDER, dnsorders}, +#endif /* INET6 */ + {NULL, 0, 0, NULL, NULL, NULL}, +}; + +struct param_ptr params5[] = +{ + {"document_root", P_STRING, PI_TEXT, (void *) &document_root, CMT_DROOT, NULL}, + {"personal_document_root", P_STRING, PI_TEXT, (void *) &personal_document_root, CMT_PDROOT, NULL}, + {"cgi_bin", P_STRING, PI_TEXT, (void *) &cgi_bin, CMT_CGIBIN, NULL}, +{"index_file", P_STRING, PI_TEXT, (void *) &index_file, CMT_IFILE, NULL}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; + +struct param_ptr params6[] = +{ + {"mime_types", P_STRING, PI_TEXT, (void *) &mimetypes_files, CMT_MIMETYPES, NULL}, + {"mailcap", P_STRING, PI_TEXT, (void *) &mailcap_files, CMT_MAILCAP, NULL}, + {"editor", P_STRING, PI_TEXT, (void *) &Editor, CMT_EDITOR, NULL}, + {"mailer", P_STRING, PI_TEXT, (void *) &Mailer, CMT_MAILER, NULL}, +{"extbrowser", P_STRING, PI_TEXT, (void *) &ExtBrowser, CMT_EXTBRZ, NULL}, + {"extbrowser2", P_STRING, PI_TEXT, (void *) &ExtBrowser2, CMT_EXTBRZ2, NULL}, + {"extbrowser3", P_STRING, PI_TEXT, (void *) &ExtBrowser3, CMT_EXTBRZ3, NULL}, + {"bgextviewer", P_INT, PI_ONOFF, (void *) &BackgroundExtViewer, CMT_BGEXTVIEW, NULL}, + {"use_lessopen", P_INT, PI_ONOFF, (void *) &use_lessopen, CMT_USE_LESSOPEN, NULL}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; + +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) +struct param_ptr params7[] = +{ + {"ssl_verify_server", P_INT, PI_ONOFF, (void *) &ssl_verify_server, CMT_SSL_VERIFY_SERVER, NULL}, + {"ssl_cert_file", P_SSLPATH, PI_TEXT, (void *) &ssl_cert_file, CMT_SSL_CERT_FILE, NULL}, + {"ssl_key_file", P_SSLPATH, PI_TEXT, (void *) &ssl_key_file, CMT_SSL_KEY_FILE, NULL}, + {"ssl_ca_path", P_SSLPATH, PI_TEXT, (void *) &ssl_ca_path, CMT_SSL_CA_PATH, NULL}, + {"ssl_ca_file", P_SSLPATH, PI_TEXT, (void *) &ssl_ca_file, CMT_SSL_CA_FILE, NULL}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; +#endif /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ + +#ifdef USE_COOKIE +struct param_ptr params8[] = +{ + {"use_cookie", P_INT, PI_ONOFF, (void *) &use_cookie, CMT_USECOOKIE, NULL}, + {"accept_cookie", P_INT, PI_ONOFF, (void *) &accept_cookie, CMT_ACCEPTCOOKIE, NULL}, + {"accept_bad_cookie", P_INT, PI_SEL_C, (void *) &accept_bad_cookie, CMT_ACCEPTBADCOOKIE, badcookiestr}, + {"cookie_reject_domains", P_STRING, PI_TEXT, (void *) &cookie_reject_domains, CMT_COOKIE_REJECT_DOMAINS, NULL}, + {"cookie_accept_domains", P_STRING, PI_TEXT, (void *) &cookie_accept_domains, CMT_COOKIE_ACCEPT_DOMAINS, NULL}, + {NULL, 0, 0, NULL, NULL, NULL}, +}; +#endif + +struct param_section sections[] = +{ +#if LANG == JA + {"ɽ���ط�", params1}, +#ifdef COLOR + {"ɽ����", params2}, +#endif /* COLOR */ + {"����¾������", params3}, + {"�ץ�����������", params4}, + {"�ǥ��쥯�ȥ�����", params5}, + {"�����ץ������", params6}, +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) + {"SSLǧ������", params7}, +#endif /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ +#ifdef USE_SSL +#define CMT_SSL_FORBID_METHOD "�Ȥ�ʤ�SSL��åɤΥꥹ��(2: SSLv2, 3: SSLv3, t:TLSv1)" +#endif +#ifdef USE_COOKIE + {"���å���������", params8}, +#endif +#else /* LANG != JA */ + {"Display", params1}, +#ifdef COLOR + {"Color Setting", params2}, +#endif /* COLOR */ + {"Other Behavior", params3}, + {"Proxy Setting", params4}, + {"Directory Setting", params5}, + {"External Programs", params6}, +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) + {"SSL Verification Setting", params7}, +#endif /* defined(USE_SSL) && + * defined(USE_SSL_VERIFY) */ +#ifdef USE_SSL +#define CMT_SSL_FORBID_METHOD N_("List of forbidden SSL method (2: SSLv2, 3: SSLv3, t:TLSv1)") +#endif +#ifdef USE_COOKIE + {"Cookie Setting", params8}, +#endif +#endif /* LANG != JA */ + {NULL, NULL} +}; + +static int +compare_table(struct rc_search_table *a, struct rc_search_table *b) +{ + return strcmp(a->param->name, b->param->name); +} + +void +create_option_search_table() +{ + int i, j, k; + int diff1, diff2; + char *p, *q; + + /* count table size */ + RC_table_size = 0; + for (j = 0; sections[j].name != NULL; j++) { + i = 0; + while (sections[j].params[i].name) { + i++; + RC_table_size++; + } + } + + RC_search_table = New_N(struct rc_search_table, RC_table_size); + k = 0; + for (j = 0; sections[j].name != NULL; j++) { + i = 0; + while (sections[j].params[i].name) { + RC_search_table[k].param = §ions[j].params[i]; + k++; + i++; + } + } + + qsort(RC_search_table, RC_table_size, sizeof(struct rc_search_table), + (int (*)(const void *, const void *)) compare_table); + + diff1 = diff2 = 0; + for (i = 0; i < RC_table_size - 1; i++) { + p = RC_search_table[i].param->name; + q = RC_search_table[i + 1].param->name; + for (j = 0; + p[j] != '\0' && + q[j] != '\0' && + p[j] == q[j]; + j++); + diff1 = j; + if (diff1 > diff2) + RC_search_table[i].uniq_pos = diff1 + 1; + else + RC_search_table[i].uniq_pos = diff2 + 1; + diff2 = diff1; + } +} + +struct param_ptr * +search_param(char *name) +{ + size_t b, e, i; + int cmp; + int len = strlen(name); + + for (b = 0, e = RC_table_size - 1; b <= e;) { + i = (b + e) / 2; + cmp = strncmp(name, RC_search_table[i].param->name, len); + + if (!cmp) { + if (len >= RC_search_table[i].uniq_pos) { + return RC_search_table[i].param; + } + else { + while ((cmp = strcmp(name, RC_search_table[i].param->name)) <= 0) + if (!cmp) + return RC_search_table[i].param; + else if (i == 0) + return NULL; + else + i--; + /* ambiguous */ + return NULL; + } + } + else if (cmp < 0) { + if (i == 0) + return NULL; + e = i - 1; + } + else + b = i + 1; + } + return NULL; +} + +#ifdef SHOW_PARAMS +void +show_params(FILE * fp) +{ + int i, j, l; + char *t; + char *cmt; + + fputs("\nconfiguration parameters\n", fp); + for (j = 0; sections[j].name != NULL; j++) { +#ifdef JP_CHARSET + if (InnerCode != DisplayCode) + cmt = conv(sections[j].name, InnerCode, DisplayCode)->ptr; + else +#endif /* JP_CHARSET */ + cmt = sections[j].name; + fprintf(fp, " section[%d]: %s\n", j, cmt); + i = 0; + while (sections[j].params[i].name) { + switch (sections[j].params[i].type) { + case P_INT: + case P_SHORT: + case P_CHARINT: + t = (sections[j].params[i].inputtype == PI_ONOFF) ? "bool" : "number"; + break; + case P_CHAR: + t = "char"; + break; + case P_STRING: + t = "string"; + break; +#if defined(USE_SSL) && defined(USE_SSL_VERIFY) + case P_SSLPATH: + t = "path"; + break; +#endif +#ifdef 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; + } +#ifdef JP_CHARSET + if (InnerCode != DisplayCode) + cmt = conv(sections[j].params[i].comment, + InnerCode, + DisplayCode)->ptr; + else +#endif /* JP_CHARSET */ + cmt = sections[j].params[i].comment; + l = 30 - (strlen(sections[j].params[i].name) + strlen(t)); + if (l < 0) + l = 1; + fprintf(fp, " -o %s=<%s>%*s%s\n", + sections[j].params[i].name, t, l, " ", cmt); + i++; + } + } +} +#endif + +static int +str_to_bool(char *value) +{ + if (value == NULL) + return 1; + switch (tolower(*value)) { + case '0': + case 'f': /* false */ + case 'n': /* no */ + case 'u': /* undef */ + return 0; + } + return 1; +} + +#ifdef 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 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 int +set_param(char *name, char *value) +{ + struct param_ptr *p; + double ppc; + + if (value == NULL) + return 0; + p = search_param(name); + if (p == NULL) + return 0; + switch (p->type) { + case P_INT: + *(int *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value) : atoi(value); + break; + case P_SHORT: + *(short *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value) : atoi(value); + break; + case P_CHARINT: + *(char *) p->varptr = (p->inputtype == PI_ONOFF) ? str_to_bool(value) : 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 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) + *(double *) p->varptr = ppc; + break; + } + return 1; +} + +static void sync_with_option(void); + +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: + sync_with_option(); + return 1; +} + +static void +interpret_rc(FILE * f) +{ + Str line; + Str tmp; + char *p; + + for (;;) { + line = Strfgets(f); + Strchop(line); + if (line->length == 0) + break; + Strremovefirstspaces(line); + if (line->ptr[0] == '#') /* comment */ + continue; + tmp = Strnew(); + p = line->ptr; + while (*p && !IS_SPACE(*p)) + Strcat_char(tmp, *p++); + while (*p && IS_SPACE(*p)) + p++; + Strlower(tmp); + set_param(tmp->ptr, p); + } +} + +void +parse_proxy() +{ + if (non_null(HTTP_proxy)) + parseURL(HTTP_proxy, &HTTP_proxy_parsed, NULL); +#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__ */ + +struct table2 * +loadMimeTypes(char *filename) +{ + FILE *f; + char *d, *type; + int i, n; + Str tmp; + struct table2 *mtypes; + + f = fopen(expandName(filename), "r"); + if (f == NULL) + return NULL; + n = 0; + while (tmp = Strfgets(f), tmp->length > 0) { + d = tmp->ptr; + if (d[0] != '#') { + d = strtok(d, " \t\n\r"); + if (d != NULL) { + d = strtok(NULL, " \t\n\r"); + for (i = 0; d != NULL; i++) + d = strtok(NULL, " \t\n\r"); + n += i; + } + } + } + fseek(f, 0, 0); + mtypes = New_N(struct table2, n + 1); + i = 0; + 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; + mtypes[i].item1 = Strnew_charp(d)->ptr; + mtypes[i].item2 = Strnew_charp(type)->ptr; + i++; + } + } + mtypes[i].item1 = NULL; + mtypes[i].item2 = NULL; + 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(struct table2 *, mimetypes_list->nitem); + for (i = 0, tl = mimetypes_list->first; tl; i++, tl = tl->next) + UserMimeTypes[i] = loadMimeTypes(tl->ptr); +} + +static void +sync_with_option(void) +{ + WrapSearch = WrapDefault; + parse_proxy(); +#ifdef USE_COOKIE + parse_cookie(); +#endif + initMailcap(); + initMimeTypes(); +} + +void +init_rc(char *config_file) +{ + struct stat st; + FILE *f; +#ifndef __EMX__ /* jsawa */ + char *tmpdir = "/tmp"; +#else + char *tmpdir; + + if ( (tmpdir = getenv("TMP")) == NULL || *tmpdir == '\0' ) + if ( (tmpdir = getenv("TEMP")) == NULL || *tmpdir == '\0' ) + if ( (tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0' ) + *tmpdir = "/tmp"; +#endif /* __EMX__ */ + + if (rc_initialized) + return; + rc_initialized = 1; + + 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)!", rc_dir); + rc_dir = tmpdir; + rc_dir_is_tmp = TRUE; + return; + } + else { + stat(rc_dir, &st); + } + } + else { + fprintf(stderr, "Can't open config directory (%s)!", rc_dir); + rc_dir = tmpdir; + rc_dir_is_tmp = TRUE; + return; + } + } + if (!S_ISDIR(st.st_mode)) { + /* not a directory */ + fprintf(stderr, "%s is not a directory!", rc_dir); + rc_dir = tmpdir; + rc_dir_is_tmp = TRUE; + return; + } + + /* open config file */ + if ((f = fopen(libFile(W3MCONFIG), "rt")) != NULL) { + interpret_rc(f); + fclose(f); + } + if (config_file == NULL) + config_file = rcFile(CONFIG_FILE); + if ((f = fopen(config_file, "rt")) != NULL) { + interpret_rc(f); + fclose(f); + } + sync_with_option(); +} + + +static char optionpanel_src1[] = +"<html><head><title>Option Setting Panel</title></head>\ +<body><center><b>Option Setting Panel</b></center><p>\n" +#ifdef __EMX__ +"<a href=\"file:///$LIB/w3mhelperpanel.exe?mode=panel\">%s</a>\n" +#else /* not __EMX__ */ +"<a href=\"file:///$LIB/w3mhelperpanel?mode=panel\">%s</a>\n" +#endif /* not __EMX__ */ +"<form method=internal action=option>"; + +static Str +to_str(struct param_ptr *p) +{ + switch (p->type) { + case P_INT: +#ifdef COLOR + case P_COLOR: +#endif + 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: + return Sprintf("%g", *(double *) p->varptr); + } + /* not reached */ + return NULL; +} + +Buffer * +load_option_panel(void) +{ + Str src = Sprintf(optionpanel_src1, CMT_HELPER); + struct param_ptr *p; + struct sel_c *s; + int x, i; + Str tmp; + + Strcat_charp(src, "<table><tr><td>"); + for (i = 0; sections[i].name != NULL; i++) { + Strcat_m_charp(src, "<h1>", + sections[i].name, + "</h1>", NULL); + p = sections[i].params; + Strcat_charp(src, "<table width=100% cellpadding=0>"); + while (p->name) { + Strcat_m_charp(src, "<tr><td>", p->comment, NULL); + Strcat(src, Sprintf("</td><td width=%d>", + (int)(28 * pixel_per_char))); + switch (p->inputtype) { + case PI_TEXT: + Strcat_m_charp(src, "<input type=text name=", + p->name, + " value=\"", + htmlquote_str( 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" : ""), + ">ON <input type=radio name=", + p->name, + " value=0", + (x ? "" : " checked"), + ">OFF", 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_charp(src, "</td></tr>\n"); + p++; + } + Strcat_charp(src, "<tr><td></td><td><p><input type=submit value=\"OK\"></td></tr>"); + Strcat_charp(src, "</table><hr width=50%>"); + } + Strcat_charp(src, "</table></form></body></html>"); + return loadHTMLString(src); +} + +void +panel_set_option(struct parsed_tagarg *arg) +{ + FILE *f = NULL; + + if (rc_dir_is_tmp) { + disp_message("There's no ~/.w3m directory... config not saved", FALSE); + } + else { + f = fopen(config_file, "wt"); + if (f == NULL) { + disp_message("Can't write option!", FALSE); + } + } + while (arg) { + if (set_param(arg->arg, arg->value)) { + if (f) + fprintf(f, "%s %s\n", arg->arg, arg->value); + } + arg = arg->next; + } + if (f) + fclose(f); + sync_with_option(); + backBf(); +} + +char * +rcFile(char *base) +{ + if (base && + (base[0] == '/' || + (base[0] == '.' && (base[1] == '/' || (base[1] == '.' && base[2] == '/'))) || + (base[0] == '~' && 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 * +libFile(char *base) +{ +#ifdef __EMX__ + Str file = Strnew_charp(get_os2_dft("W3M_LIB_DIR", LIB_DIR)); +#else + Str file = Strnew_charp(LIB_DIR); +#endif /* __EMX__ */ + + Strcat_char(file, '/'); + Strcat_charp(file, base); + return expandName(file->ptr); +} + +char * +helpFile(char *base) +{ +#ifdef __EMX__ + Str file = Strnew_charp(get_os2_dft("W3M_HELP_DIR", HELP_DIR)); +#else /* not __EMX__ */ + Str file = Strnew_charp(HELP_DIR); +#endif /* not __EMX__ */ + Strcat_char(file, '/'); + Strcat_charp(file, base); + return expandName(file->ptr); +} diff --git a/rcparams.h b/rcparams.h new file mode 100644 index 0000000..099fefe --- /dev/null +++ b/rcparams.h @@ -0,0 +1,76 @@ +/* params1 */ + +global int Tabstop init(8); +#ifdef JP_CHARSET +extern char DisplayCode; +#endif /* JP_CHARSET */ +global char RenderFrame init(FALSE); +global char TargetSelf init(FALSE); +global int displayLink init(FALSE); +global int UseExternalDirBuffer init(TRUE); +#ifdef __EMX__ +global char *DirBufferCommand init("file:///$LIB/dirlist.cmd"); +#else +global char *DirBufferCommand init("file:///$LIB/dirlist.cgi"); +#endif +global int multicolList init(FALSE); + +/* params2 */ +#ifdef COLOR +global int useColor init(TRUE); +global int basic_color init(8); /* don't change */ +global int anchor_color init(4); /* blue */ +global int image_color init(2); /* green */ +global int form_color init(1); /* red */ +#ifdef BG_COLOR +global int bg_color init(8); /* don't change */ +#endif /* BG_COLOR */ +global int useActiveColor init(FALSE); +global int active_color init(6); /* cyan */ +global int useVisitedColor init(FALSE); +global int visited_color init(5); /* magenta */ +#endif /* COLOR */ + +/* params3 */ + +global int PagerMax init(PAGER_MAX_LINE); +#ifdef USE_HISTORY +global int URLHistSize init(100); +global int SaveURLHist init(TRUE); +#endif /* USE_HISTORY */ +global int confirm_on_quit init(TRUE); +global int showLineNum init(FALSE); +global char *ftppasswd init(NULL); +global char *UserAgent init(NULL); +global char *AcceptLang init(NULL); +global int WrapDefault init(FALSE); +#ifdef USE_COOKIE +global int use_cookie init(TRUE); +global int accept_bad_cookie init(FALSE); +#endif /* USE_COOKIE */ + +/* params4 */ + +global char *HTTP_proxy init(NULL); +global char *GOPHER_proxy init(NULL); +global char *FTP_proxy init(NULL); +global int NOproxy_netaddr init(TRUE); +global char *NO_proxy init(NULL); +#ifdef INET6 +global int DNS_order init(0); +#endif /* INET6 */ + +/* params5 */ + +global char *document_root init(NULL); +global char *personal_document_root init(NULL); +global char *cgi_bin init(NULL); + +/* params6 */ + +global char *Editor init(DEF_EDITOR); +global char *Mailer init(DEF_MAILER); +global char *ExtBrowser init(DEF_EXT_BROWSER); +global char *ExtBrowser2 init(NULL); +global char *ExtBrowser3 init(NULL); +global int BackgroundExtViewer init(TRUE); @@ -0,0 +1,433 @@ +/* + * regex: Regular expression pattern match library + * + * by A.ITO, December 1989 + */ + +#ifdef REGEX_DEBUG +#include <sys/types.h> +#include <malloc.h> +#endif /* REGEX_DEBUG */ +#include <ctype.h> +#include <gc.h> +#ifdef __EMX__ +#include <strings.h> +#endif +#include "fm.h" +#include "regex.h" + +#ifdef JP_CHARSET +#define RE_KANJI(p) (((unsigned char)*(p) << 8) | (unsigned char)*((p)+1)) +#endif + +#define RE_WHICH_RANGE 0xffff + +static Regex DefaultRegex; +#define CompiledRegex DefaultRegex.re +#define Cstorage DefaultRegex.storage + +static longchar *st_ptr; + +static int regmatch(regexchar *, char *, int, int, char **); +static int regmatch1(regexchar *, longchar); +static int matchWhich(longchar *, longchar); + + +/* + * regexCompile: compile regular expression + */ +char * +regexCompile(char *ex, int igncase) +{ + char *msg; + newRegex(ex, igncase, &DefaultRegex, &msg); + return msg; +} + +Regex * +newRegex(char *ex, int igncase, Regex * regex, char **msg) +{ + char *p; + longchar *r; + regexchar *re = regex->re - 1; + int m; + + if (regex == 0) + regex = (Regex *) GC_malloc_atomic(sizeof(Regex)); + st_ptr = regex->storage; + for (p = ex; *p != '\0'; p++) { + switch (*p) { + case '.': + re++; + re->pattern = NULL; + re->mode = RE_ANY; + break; + case '$': + re++; + re->pattern = NULL; + re->mode = RE_END; + break; + case '^': + re++; + re->pattern = NULL; + re->mode = RE_BEGIN; + break; + case '*': + if (!(re->mode & RE_ANY) && re->pattern == NULL) { + if (msg) + *msg = "Invalid regular expression"; + return NULL; + } + re->mode |= RE_ANYTIME; + break; + case '[': + r = st_ptr; + if (*++p == '^') { + p++; + m = RE_EXCEPT; + } + else + m = RE_WHICH; + while (*p != ']') { + if (*p == '\\') { + *(st_ptr++) = *(p + 1); + p += 2; + } + else if (*p == '-') { + *(st_ptr++) = RE_WHICH_RANGE; + p++; + } + else if (*p == '\0') { + if (msg) + *msg = "Missing ]"; + return NULL; + } +#ifdef JP_CHARSET + else if (IS_KANJI1(*p)) { + *(st_ptr++) = RE_KANJI(p); + p += 2; + } +#endif + else + *(st_ptr++) = (unsigned char)*(p++); + } + *(st_ptr++) = '\0'; + re++; + re->pattern = r; + re->mode = m; + break; + case '\\': + p++; + default: + re++; +#ifdef JP_CHARSET + if (IS_KANJI1(*p)) { + *(st_ptr) = RE_KANJI(p); + p++; + } + else +#endif + *st_ptr = (unsigned char)*p; + re->pattern = st_ptr; + st_ptr++; + re->mode = RE_NORMAL; + if (igncase) + re->mode |= RE_IGNCASE; + } + if (st_ptr >= &Cstorage[STORAGE_MAX] || + re >= &CompiledRegex[REGEX_MAX]) { + if (msg) + *msg = "Regular expression too long"; + return NULL; + } + } + re++; + re->mode = RE_ENDMARK; + if (msg) + *msg = NULL; + return regex; +} + +/* + * regexMatch: match regular expression + */ +int +regexMatch(char *str, int len, int firstp) +{ + return RegexMatch(&DefaultRegex, str, len, firstp); +} + +int +RegexMatch(Regex * re, char *str, int len, int firstp) +{ + char *p, *ep; + + if (str == NULL) + return 0; + re->position = NULL; + ep = str + ((len == 0) ? strlen(str) : len); + for (p = str; p < ep; p++) { + switch (regmatch(re->re, p, ep - p, firstp && (p == str), &re->lposition)) { + case 1: + re->position = p; + return 1; + case -1: + re->position = NULL; + return -1; + } +#ifdef JP_CHARSET + if (IS_KANJI1(*p)) + p++; +#endif + } + return 0; +} + +/* + * matchedPosition: last matched position + */ +void +MatchedPosition(Regex * re, char **first, char **last) +{ + *first = re->position; + *last = re->lposition; +} + +void +matchedPosition(char **first, char **last) +{ + *first = DefaultRegex.position; + *last = DefaultRegex.lposition; +} + +/* + * Intermal routines + */ +static int +regmatch(regexchar * re, char *str, int len, int firstp, char **lastpos) +{ + char *p = str, *ep = str + len; + char *lpos, *llpos = NULL; + longchar k; + +#ifdef REGEX_DEBUG + debugre(re, str); +#endif /* REGEX_DEBUG */ + while ((re->mode & RE_ENDMARK) == 0) { + if (re->mode & RE_BEGIN) { + if (!firstp) + return 0; + re++; + } + else if (re->mode & RE_ANYTIME) { + short matched = 0, ok = 0; + do { + if (regmatch(re + 1, p, ep - p, firstp, &lpos) == 1) { + llpos = lpos; + matched = 1; + } + else if (matched) { + ok = 1; + break; + } + if (p >= ep) { + if (matched) + ok = 1; + break; + } +#ifdef JP_CHARSET + if (IS_KANJI1(*p)) { + k = RE_KANJI(p); + if (regmatch1(re, k)) { + if (lastpos != NULL) + *lastpos = llpos; + p += 2; + } + else if (matched) + ok = 1; + else + break; + } + else +#endif + { + k = (unsigned char)*p; + if (regmatch1(re, k)) { + p++; + if (lastpos != NULL) + *lastpos = llpos; + } + else if (matched) + ok = 1; + else + break; + } + } while (!ok); + if (lastpos != NULL) + *lastpos = llpos; + return ok; + } + else if (re->mode & RE_END) { + if (lastpos != NULL) + *lastpos = p; + return (p >= ep); + } + else { + int a; +#ifdef JP_CHARSET + if (IS_KANJI1(*p)) { + k = RE_KANJI(p); + p += 2; + a = regmatch1(re, k); + } + else +#endif + { + k = (unsigned char)*(p++); + a = regmatch1(re, k); + } + if (!a) + return 0; + else + re++; + } + } + if (lastpos != NULL) + *lastpos = p; + return 1; +} + +static int +regmatch1(regexchar * re, longchar c) +{ + switch (re->mode & RE_MATCHMODE) { + case RE_ANY: +#ifdef REGEX_DEBUG + printf("%c vs any. -> 1\n", c); +#endif /* REGEX_DEBUG */ + return 1; + case RE_NORMAL: +#ifdef REGEX_DEBUG + printf("RE=%c vs %c -> %d\n", *re->pattern, c, *re->pattern == c); +#endif /* REGEX_DEBUG */ + if (re->mode & RE_IGNCASE) { + if (*re->pattern < 127 && c < 127 && + IS_ALPHA(*re->pattern) && IS_ALPHA(c)) + return tolower(*re->pattern) == tolower(c); + else + return *re->pattern == c; + } + else + return (*re->pattern == c); + case RE_WHICH: + return matchWhich(re->pattern, c); + case RE_EXCEPT: + return !matchWhich(re->pattern, c); + } + return 0; +} + +static int +matchWhich(longchar * pattern, longchar c) +{ + longchar *p = pattern; + int ans = 0; + +#ifdef REGEX_DEBUG + printf("RE pattern = %s char=%c", pattern, c); +#endif /* REGEX_DEBUG */ + while (*p != '\0') { + if (*(p + 1) == RE_WHICH_RANGE && *(p + 2) != '\0') { /* Char * + * + * * * * + * * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * + * * * * * + * * * * * + * * * + * class. + * * * * * + * * * * * + * * * * * + */ + if (*p <= c && c <= *(p + 2)) { + ans = 1; + break; + } + p += 3; + } + else { + if (*p == c) { + ans = 1; + break; + } + p++; + } + } +#ifdef REGEX_DEBUG + printf(" -> %d\n", ans); +#endif /* REGEX_DEBUG */ + return ans; +} + +#ifdef REGEX_DEBUG +char * +lc2c(longchar * x) +{ + static char y[100]; + int i = 0; + + while (x[i]) { + if (x[i] == RE_WHICH_RANGE) + y[i] = '-'; + else + y[i] = x[i]; + i++; + } + y[i] = '\0'; + return y; +} + +void +debugre(re, s) + regexchar *re; + char *s; +{ + for (; !(re->mode & RE_ENDMARK); re++) { + if (re->mode & RE_BEGIN) { + printf("Begin "); + continue; + } + else if (re->mode & RE_END) { + printf("End "); + continue; + } + if (re->mode & RE_ANYTIME) + printf("Anytime-"); + + switch (re->mode & RE_MATCHMODE) { + case RE_ANY: + printf("Any "); + break; + case RE_NORMAL: + printf("Match-to'%c' ", *re->pattern); + break; + case RE_WHICH: + printf("One-of\"%s\" ", lc2c(re->pattern)); + break; + case RE_EXCEPT: + printf("Other-than\"%s\" ", lc2c(re->pattern)); + break; + default: + printf("Unknown "); + } + } + putchar('\n'); +} + +#endif /* REGEX_DEBUG */ @@ -0,0 +1,56 @@ +#define REGEX_MAX 64 +#define STORAGE_MAX 256 + +#ifndef NULL +#define NULL 0 +#endif /* not NULL */ + +#define RE_NORMAL 0 +#define RE_MATCHMODE 0x07 +#define RE_ANY 0x01 +#define RE_WHICH 0x02 +#define RE_EXCEPT 0x04 +#define RE_ANYTIME 0x08 +#define RE_BEGIN 0x10 +#define RE_END 0x20 +#define RE_IGNCASE 0x40 +#define RE_ENDMARK 0x80 + +typedef unsigned short longchar; + + +typedef struct { + + longchar *pattern; + + unsigned char mode; + +} regexchar; + + +typedef struct { + + regexchar re[REGEX_MAX]; + + longchar storage[STORAGE_MAX]; + + char *position; + + char *lposition; + +} Regex; + + +Regex *newRegex(char *ex, int igncase, Regex * regex, char **error_msg); + +int RegexMatch(Regex * re, char *str, int len, int firstp); + +void MatchedPosition(Regex * re, char **first, char **last); + + +/* backward compatibility */ +char *regexCompile(char *ex, int igncase); + +int regexMatch(char *str, int len, int firstp); + +void matchedPosition(char **first, char **last); diff --git a/scripts/bm2menu.pl b/scripts/bm2menu.pl new file mode 100644 index 0000000..1390bae --- /dev/null +++ b/scripts/bm2menu.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +$PRE_MENU = ""; +$POST_MENU = <<EOF; + nop "����������������������" + func "�֥å��ޡ������ɲ� (a)" ADD_BOOKMARK "aA" +EOF + +@section = (); +%title = (); +%url = (); +while(<>) { + if (/<h2>(.*)<\/h2>/) { + $s = &unquote($1); + push(@section, $s); + } elsif (/<li><a href=\"(.*)\">(.*)<\/a>/) { + $u = &unquote($1); + $t = &unquote($2); + $url{$s} .= "$u\n"; + $title{$s} .= "$t\n"; + } +} + +print "menu Bookmarks\n"; +print $PRE_MENU; +foreach(@section) { + print " popup\t\"$_\"\t\"$_\"\n"; +} +print $POST_MENU; +print "end\n"; + +foreach(@section) { + print "\n"; + print "menu \"$_\"\n"; + @ts = split("\n", $title{$_}); + @us = split("\n", $url{$_}); + while(@ts) { + $t = shift @ts; + $u = shift @us; + print " func\t\"$t\"\tGOTO\t\"\"\t\"$u\"\n"; + } + print "end\n"; +} + +sub unquote { + local($_) = @_; + + s/\</\</g; + s/\>/\>/g; + s/\ / /g; + s/\&/\&/g; + + return $_; +} diff --git a/scripts/dirlist.cgi b/scripts/dirlist.cgi new file mode 100644 index 0000000..40f11a8 --- /dev/null +++ b/scripts/dirlist.cgi @@ -0,0 +1,501 @@ +#!/usr/local/bin/perl +# +# Directory list CGI by Hironori Sakamoto (hsaka@mth.biglobe.ne.jp) +# + +$CYGWIN = 0; +$RC_DIR = '~/.w3m/'; + +$RC_DIR =~ s@^~/@$ENV{'HOME'}/@; +$CONFIG = "$RC_DIR/dirlist"; +$CGI = $ENV{'SCRIPT_NAME'} || $0; +$CGI = &html_quote(&form_encode("file:$CGI")); + +$AFMT = '<a href="%s"><nobr>%s</nobr></a>'; +$NOW = time(); + +@OPT = &init_option($CONFIG); + +$query = $ENV{'QUERY_STRING'}; +$cmd = ''; +$cgi = 0; +if ($query eq '') { + $_ = `pwd`; + chop; + s/\r$//; + $dir = $_; + $cgi = 0; +} elsif ($query =~ /^(opt\d+|dir|cmd)=/) { + foreach(split(/\&/, $query)) { + if (s/^dir=//) { + $dir = &form_decode($_); + } elsif (s/^opt(\d+)=//) { + $OPT[$1] = $_; + } elsif (s/^cmd=//) { + $cmd = $_; + } + } + $cgi = 1; +} else { + $dir = $query; + if (($dir !~ m@^/@) && + ($CYGWIN && $dir !~ /^[a-z]:/i)) { + $_ = `pwd`; + chop; + s/\r$//; + $dir = "$_/$dir"; + } + $cgi = -1; +} +if ($dir !~ m@/$@) { + $dir .= '/'; +} +$ROOT = ''; +if ($CYGWIN) { + if (($dir =~ s@^//[^/]+@@) || ($dir =~ s@^[a-z]:@@i)) { + $ROOT = $&; + } +} +if ($cgi) { + $dir = &cleanup($dir); +} + +$TYPE = $OPT[$OPT_TYPE]; +$FORMAT = $OPT[$OPT_FORMAT]; +$SORT = $OPT[$OPT_SORT]; +if ($cmd) { + &update_option($CONFIG); +} + +$qdir = &html_quote("$ROOT$dir"); +$edir = &html_quote(&form_encode("$ROOT$dir")); +if (! opendir(DIR, "$ROOT$dir")) { + print <<EOF; +Content-Type: text/html + +<html> +<head> +<title>Directory list of $qdir</title> +</head> +<body> +<b>$qdir</b>: $! ! +</body> +</html> +EOF + exit 1; +} + +# ($cgi > 0) && print <<EOF; +# w3m-control: DELETE_PREVBUF +# EOF +print <<EOF; +Content-Type: text/html + +<html> +<head> +<title>Directory list of $qdir</title> +</head> +<body> +<h1>Directory list of $qdir</h1> +EOF +&print_form($qdir, @OPT); +print <<EOF; +<hr> +EOF +$dir =~ s@/$@@; +@sdirs = split('/', $dir); +$_ = $sdirs[0]; +if ($_ eq '') { + $_ = '/'; +} +if ($TYPE eq $TYPE_TREE) { + print <<EOF; +<table hborder width="640"> +<tr valign=top><td width="160"> +<pre> +EOF + $q = &html_quote("$ROOT$_"); + $e = &html_quote(&form_encode("$ROOT$_")); + if ($dir =~ m@^$@) { + $n = "\" name=\"current"; + } else { + $n = ''; + } + printf("$AFMT\n", "$q$n", "<b>$q</b>"); + $N = 0; + $SKIPLINE = ""; + + &left_dir('', @sdirs); + + print <<EOF; +</pre> +</td><td width="400"> +<pre>$SKIPLINE +EOF +} else { + print <<EOF; +<pre> +EOF +} + +&right_dir($dir); + +if ($TYPE eq $TYPE_TREE) { + print <<EOF; +</pre> +</td></tr> +</table> +</body> +</html> +EOF +} else { + print <<EOF; +</pre> +</body> +</html> +EOF +} + +sub left_dir { + local($pre, $dir, @sdirs) = @_; + local($ok) = (@sdirs == 0); + local(@cdirs) = (); + local($_, $dir0, $d, $qdir, $q, $edir, $e); + + $dir0 = "$dir/"; + $dir = "$ROOT$dir0"; + opendir(DIR, $dir) || return; + + foreach(sort readdir(DIR)) { + -d "$dir$_" || next; + /^\.$/ && next; + /^\.\.$/ && next; + push(@cdirs, $_); + } + closedir(DIR); + + $qdir = &html_quote($dir); + $edir = &html_quote(&form_encode($dir)); + while(@cdirs) { + $_ = shift @cdirs; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + $N++; + if (!$ok && $_ eq $sdirs[0]) { + $d = $dir0 . shift @sdirs; + if (!@sdirs) { + $n = "\" name=\"current"; + $SKIPLINE = "\n" x $N; + } else { + $n = ''; + } + printf("${pre}o-$AFMT\n", "$qdir$q$n", "<b>$q</b>"); + &left_dir(@cdirs ? "$pre| " : "$pre ", $d, @sdirs); + $ok = 1; + } else { + printf("${pre}+-$AFMT\n", "$qdir$q", $q); + } + } +} + +sub right_dir { + local($dir) = @_; + local(@list); + local($_, $qdir, $q, $edir, $e, $f, $max, @d, $type, $u, $g); + local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks); + local(%sizes, %ctimes, %prints); + + $dir = "$ROOT$dir/"; + opendir(DIR, $dir) || return; + + $qdir = &html_quote($dir); + $edir = &html_quote(&form_encode($dir)); + if ($TYPE eq $TYPE_TREE) { + print "<b>$qdir</b>\n"; + } + @list = (); + $max = 0; + foreach(readdir(DIR)) { + /^\.$/ && next; +# if ($TYPE eq $TYPE_TREE) { +# /^\.\.$/ && next; +# } + $f = "$dir$_"; + (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f)) || next; + push(@list, $_); + $sizes{$_} = $size; + $ctimes{$_} = $ctime; + + if ($FORMAT eq $FORMAT_COLUMN) { + if (length($_) > $max) { + $max = length($_); + } + next; + } + $type = &utype($mode); + if ($FORMAT eq $FORMAT_SHORT) { + $prints{$_} = sprintf("%-6s ", "[$type]"); + next; + } + if ($type =~ /^[CB]/) { + $size = sprintf("%3u, %3u", ($rdev >> 8) & 0xff, $rdev & 0xffff00ff); + } + if ($FORMAT eq $FORMAT_LONG) { + $u = $USER{$uid} || ($USER{$uid} = getpwuid($uid) || $uid); + $g = $GROUP{$gid} || ($GROUP{$gid} = getgrgid($gid) || $gid); + $prints{$_} = sprintf( "%s %-8s %-8s %8s %s ", + &umode($mode), $u, $g, $size, &utime($ctime)); +# } elsif ($FORMAT eq $FORMAT_STANDARD) { + } else { + $prints{$_} = sprintf("%-6s %8s %s ", "[$type]", $size, &utime($ctime)); + } + } + closedir(DIR); + if ($SORT eq $SORT_SIZE) { + @list = sort { $sizes{$b} <=> $sizes{$a} || $a <=> $b } @list; + } elsif ($SORT eq $SORT_TIME) { + @list = sort { $ctimes{$b} <=> $ctimes{$a} || $a <=> $b } @list; + } else { + @list = sort @list; + } + if ($FORMAT eq $FORMAT_COLUMN) { + local($COLS, $l, $nr, $n); + if ($TYPE eq $TYPE_TREE) { + $COLS = 60; + } else { + $COLS = 80; + } + $l = int($COLS / ($max + 2)) || 1; + $nr = int($#list / $l + 1); + $n = 0; + print "<table>\n<tr valign=top>"; + foreach(@list) { + $f = "$dir$_"; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + if ($n % $nr == 0) { + print "<td>"; + } + if (-d $f) { + printf($AFMT, "$qdir$q", "$q/"); + } else { + printf($AFMT, "$qdir$q", $q); + } + $n++; + if ($n % $nr == 0) { + print "</td>\n"; + } else { + print "<br>\n"; + } + } + print "</tr></table>\n"; + return; + } + foreach(@list) { + $f = "$dir$_"; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + print $prints{$_}; + if (-d $f) { + printf($AFMT, "$qdir$q", "$q/"); + } else { + printf($AFMT, "$qdir$q", $q); + } + if (-l $f) { + print " -> ", &html_quote(readlink($f)); + } + print "\n"; + } +} + +sub init_option { + local($config) = @_; + $OPT_TYPE = 0; + $OPT_FORMAT = 1; + $OPT_SORT = 2; + $TYPE_TREE = 't'; + $TYPE_STARDRD = 'd'; + $FORMAT_SHORT = 's'; + $FORMAT_STANDRAD = 'd'; + $FORMAT_LONG = 'l'; + $FORMAT_COLUMN = 'c'; + $SORT_NAME = 'n'; + $SORT_SIZE = 's'; + $SORT_TIME = 't'; + local(@opt) = ($TYPE_TREE, $FORMAT_STANDRAD, $SORT_NAME); + local($_); + + open(CONFIG, "< $config") || return @opt; + while(<CONFIG>) { + chop; + s/^\s+//; + tr/A-Z/a-z/; + if (/^type\s+(\S)/i) { + $opt[$OPT_TYPE] = $1; + } elsif (/^format\s+(\S)/i) { + $opt[$OPT_FORMAT] = $1 + } elsif (/^sort\s+(\S)/i) { + $opt[$OPT_SORT] = $1; + } + } + close(CONFIG); + return @opt; +} + +sub update_option { + local($config) = @_; + + open(CONFIG, "> $config") || return; + print CONFIG <<EOF; +type $TYPE +format $FORMAT +sort $SORT +EOF + close(CONFIG); +} + +sub print_form { + local($d, @OPT) = @_; + local(@disc) = ('Type', 'Format', 'Sort'); + local(@val) = ( + "('t', 'd')", + "('s', 'd', 'c')", + "('n', 's', 't')", + ); + local(@opt) = ( + "('Tree', 'Standard')", + "('Short', 'Standard', 'Column')", + "('By Name', 'By Size', 'By Time')" + ); + local($_, @vs, @os, $v, $o); + + print <<EOF; +<form action=\"$CGI\"> +<center> +<table> +<tr valign=top> +EOF + foreach(0 .. 2) { + print "<td align> $disc[$_]</td>\n"; + } + print "</tr><tr>\n"; + foreach(0 .. 2) { + print "<td><select name=opt$_>\n"; + eval "\@vs = $val[$_]"; + eval "\@os = $opt[$_]"; + foreach $v (@vs) { + $o = shift(@os); + if ($v eq $OPT[$_]) { + print "<option value=$v selected>$o\n"; + } else { + print "<option value=$v>$o\n"; + } + } + print "</select></td>\n"; + } + print <<EOF; +<td><input type=submit name=cmd value="Update"></td> +</tr> +</table> +</center> +<input type=hidden name=dir value="$d"> +</form> +EOF +} + +sub html_quote { + local($_) = @_; + local(%QUOTE) = ( + '<', '<', + '>', '>', + '&', '&', + '"', '"', + ); + s/[<>&"]/$QUOTE{$&}/g; + return $_; +} +sub form_encode { + local($_) = @_; + s/[\t\r\n%#&=+]/sprintf('%%%2x', unpack('c', $&))/eg; + s/ /+/g; + return $_; +} + +sub form_decode { + local($_) = @_; + s/\+/ /g; + s/%([\da-f][\da-f])/pack('c', hex($1))/egi; + return $_; +} + +sub cleanup { + local($_) = @_; + + s@//+@/@g; + s@/\./@/@g; + while(m@/\.\./@) { + s@^/(\.\./)+@/@; + s@/[^/]+/\.\./@/@; + } + return $_; +} + +sub utype { + local($_) = @_; + local(%T) = ( + 0010000, 'PIPE', + 0020000, 'CHR', + 0040000, 'DIR', + 0060000, 'BLK', + 0100000, 'FILE', + 0120000, 'LINK', + 0140000, 'SOCK', + ); + return $T{($_ & 0170000)} || 'FILE'; +} + +sub umode { + local($_) = @_; + local(%T) = ( + 0010000, 'p', + 0020000, 'c', + 0040000, 'd', + 0060000, 'b', + 0100000, '-', + 0120000, 'l', + 0140000, 's', + ); + + return ($T{($_ & 0170000)} || '-') + . (($_ & 00400) ? 'r' : '-') + . (($_ & 00200) ? 'w' : '-') + . (($_ & 04000) ? 's' : + (($_ & 00100) ? 'x' : '-')) + . (($_ & 00040) ? 'r' : '-') + . (($_ & 00020) ? 'w' : '-') + . (($_ & 02000) ? 's' : + (($_ & 00010) ? 'x' : '-')) + . (($_ & 00004) ? 'r' : '-') + . (($_ & 00002) ? 'w' : '-') + . (($_ & 01000) ? 't' : + (($_ & 00001) ? 'x' : '-')); +} + +sub utime { + local($_) = @_; + local(@MON) = ( + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ); + local($sec,$min,$hour,$mday,$mon, + $year,$wday,$yday,$isdst) = localtime($_); + + if ($_ > $NOW - 182*24*60*60 && $_ < $NOW + 183*24*60*60) { + return sprintf("%3s %2d %.2d:%.2d", $MON[$mon], $mday, $hour, $min); + } else { + return sprintf("%3s %2d %5d", $MON[$mon], $mday, 1900+$year); + } +} + diff --git a/scripts/dirlist.in b/scripts/dirlist.in new file mode 100755 index 0000000..991ba95 --- /dev/null +++ b/scripts/dirlist.in @@ -0,0 +1,501 @@ +#!@PERL@ +# +# Directory list CGI by Hironori Sakamoto (hsaka@mth.biglobe.ne.jp) +# + +$CYGWIN = @CYGWIN@; +$RC_DIR = '~/.w3m/'; + +$RC_DIR =~ s@^~/@$ENV{'HOME'}/@; +$CONFIG = "$RC_DIR/dirlist"; +$CGI = $ENV{'SCRIPT_NAME'} || $0; +$CGI = &html_quote(&form_encode("file:$CGI")); + +$AFMT = '<a href="%s"><nobr>%s</nobr></a>'; +$NOW = time(); + +@OPT = &init_option($CONFIG); + +$query = $ENV{'QUERY_STRING'}; +$cmd = ''; +$cgi = 0; +if ($query eq '') { + $_ = `pwd`; + chop; + s/\r$//; + $dir = $_; + $cgi = 0; +} elsif ($query =~ /^(opt\d+|dir|cmd)=/) { + foreach(split(/\&/, $query)) { + if (s/^dir=//) { + $dir = &form_decode($_); + } elsif (s/^opt(\d+)=//) { + $OPT[$1] = $_; + } elsif (s/^cmd=//) { + $cmd = $_; + } + } + $cgi = 1; +} else { + $dir = $query; + if (($dir !~ m@^/@) && + ($CYGWIN && $dir !~ /^[a-z]:/i)) { + $_ = `pwd`; + chop; + s/\r$//; + $dir = "$_/$dir"; + } + $cgi = -1; +} +if ($dir !~ m@/$@) { + $dir .= '/'; +} +$ROOT = ''; +if ($CYGWIN) { + if (($dir =~ s@^//[^/]+@@) || ($dir =~ s@^[a-z]:@@i)) { + $ROOT = $&; + } +} +if ($cgi) { + $dir = &cleanup($dir); +} + +$TYPE = $OPT[$OPT_TYPE]; +$FORMAT = $OPT[$OPT_FORMAT]; +$SORT = $OPT[$OPT_SORT]; +if ($cmd) { + &update_option($CONFIG); +} + +$qdir = &html_quote("$ROOT$dir"); +$edir = &html_quote(&form_encode("$ROOT$dir")); +if (! opendir(DIR, "$ROOT$dir")) { + print <<EOF; +Content-Type: text/html + +<html> +<head> +<title>Directory list of $qdir</title> +</head> +<body> +<b>$qdir</b>: $! ! +</body> +</html> +EOF + exit 1; +} + +# ($cgi > 0) && print <<EOF; +# w3m-control: DELETE_PREVBUF +# EOF +print <<EOF; +Content-Type: text/html + +<html> +<head> +<title>Directory list of $qdir</title> +</head> +<body> +<h1>Directory list of $qdir</h1> +EOF +&print_form($qdir, @OPT); +print <<EOF; +<hr> +EOF +$dir =~ s@/$@@; +@sdirs = split('/', $dir); +$_ = $sdirs[0]; +if ($_ eq '') { + $_ = '/'; +} +if ($TYPE eq $TYPE_TREE) { + print <<EOF; +<table hborder width="640"> +<tr valign=top><td width="160"> +<pre> +EOF + $q = &html_quote("$ROOT$_"); + $e = &html_quote(&form_encode("$ROOT$_")); + if ($dir =~ m@^$@) { + $n = "\" name=\"current"; + } else { + $n = ''; + } + printf("$AFMT\n", "$q$n", "<b>$q</b>"); + $N = 0; + $SKIPLINE = ""; + + &left_dir('', @sdirs); + + print <<EOF; +</pre> +</td><td width="400"> +<pre>$SKIPLINE +EOF +} else { + print <<EOF; +<pre> +EOF +} + +&right_dir($dir); + +if ($TYPE eq $TYPE_TREE) { + print <<EOF; +</pre> +</td></tr> +</table> +</body> +</html> +EOF +} else { + print <<EOF; +</pre> +</body> +</html> +EOF +} + +sub left_dir { + local($pre, $dir, @sdirs) = @_; + local($ok) = (@sdirs == 0); + local(@cdirs) = (); + local($_, $dir0, $d, $qdir, $q, $edir, $e); + + $dir0 = "$dir/"; + $dir = "$ROOT$dir0"; + opendir(DIR, $dir) || return; + + foreach(sort readdir(DIR)) { + -d "$dir$_" || next; + /^\.$/ && next; + /^\.\.$/ && next; + push(@cdirs, $_); + } + closedir(DIR); + + $qdir = &html_quote($dir); + $edir = &html_quote(&form_encode($dir)); + while(@cdirs) { + $_ = shift @cdirs; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + $N++; + if (!$ok && $_ eq $sdirs[0]) { + $d = $dir0 . shift @sdirs; + if (!@sdirs) { + $n = "\" name=\"current"; + $SKIPLINE = "\n" x $N; + } else { + $n = ''; + } + printf("${pre}o-$AFMT\n", "$qdir$q$n", "<b>$q</b>"); + &left_dir(@cdirs ? "$pre| " : "$pre ", $d, @sdirs); + $ok = 1; + } else { + printf("${pre}+-$AFMT\n", "$qdir$q", $q); + } + } +} + +sub right_dir { + local($dir) = @_; + local(@list); + local($_, $qdir, $q, $edir, $e, $f, $max, @d, $type, $u, $g); + local($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks); + local(%sizes, %ctimes, %prints); + + $dir = "$ROOT$dir/"; + opendir(DIR, $dir) || return; + + $qdir = &html_quote($dir); + $edir = &html_quote(&form_encode($dir)); + if ($TYPE eq $TYPE_TREE) { + print "<b>$qdir</b>\n"; + } + @list = (); + $max = 0; + foreach(readdir(DIR)) { + /^\.$/ && next; +# if ($TYPE eq $TYPE_TREE) { +# /^\.\.$/ && next; +# } + $f = "$dir$_"; + (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, + $atime,$mtime,$ctime,$blksize,$blocks) = lstat($f)) || next; + push(@list, $_); + $sizes{$_} = $size; + $ctimes{$_} = $ctime; + + if ($FORMAT eq $FORMAT_COLUMN) { + if (length($_) > $max) { + $max = length($_); + } + next; + } + $type = &utype($mode); + if ($FORMAT eq $FORMAT_SHORT) { + $prints{$_} = sprintf("%-6s ", "[$type]"); + next; + } + if ($type =~ /^[CB]/) { + $size = sprintf("%3u, %3u", ($rdev >> 8) & 0xff, $rdev & 0xffff00ff); + } + if ($FORMAT eq $FORMAT_LONG) { + $u = $USER{$uid} || ($USER{$uid} = getpwuid($uid) || $uid); + $g = $GROUP{$gid} || ($GROUP{$gid} = getgrgid($gid) || $gid); + $prints{$_} = sprintf( "%s %-8s %-8s %8s %s ", + &umode($mode), $u, $g, $size, &utime($ctime)); +# } elsif ($FORMAT eq $FORMAT_STANDARD) { + } else { + $prints{$_} = sprintf("%-6s %8s %s ", "[$type]", $size, &utime($ctime)); + } + } + closedir(DIR); + if ($SORT eq $SORT_SIZE) { + @list = sort { $sizes{$b} <=> $sizes{$a} || $a <=> $b } @list; + } elsif ($SORT eq $SORT_TIME) { + @list = sort { $ctimes{$b} <=> $ctimes{$a} || $a <=> $b } @list; + } else { + @list = sort @list; + } + if ($FORMAT eq $FORMAT_COLUMN) { + local($COLS, $l, $nr, $n); + if ($TYPE eq $TYPE_TREE) { + $COLS = 60; + } else { + $COLS = 80; + } + $l = int($COLS / ($max + 2)) || 1; + $nr = int($#list / $l + 1); + $n = 0; + print "<table>\n<tr valign=top>"; + foreach(@list) { + $f = "$dir$_"; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + if ($n % $nr == 0) { + print "<td>"; + } + if (-d $f) { + printf($AFMT, "$qdir$q", "$q/"); + } else { + printf($AFMT, "$qdir$q", $q); + } + $n++; + if ($n % $nr == 0) { + print "</td>\n"; + } else { + print "<br>\n"; + } + } + print "</tr></table>\n"; + return; + } + foreach(@list) { + $f = "$dir$_"; + $q = &html_quote($_); + $e = &html_quote(&form_encode($_)); + print $prints{$_}; + if (-d $f) { + printf($AFMT, "$qdir$q", "$q/"); + } else { + printf($AFMT, "$qdir$q", $q); + } + if (-l $f) { + print " -> ", &html_quote(readlink($f)); + } + print "\n"; + } +} + +sub init_option { + local($config) = @_; + $OPT_TYPE = 0; + $OPT_FORMAT = 1; + $OPT_SORT = 2; + $TYPE_TREE = 't'; + $TYPE_STARDRD = 'd'; + $FORMAT_SHORT = 's'; + $FORMAT_STANDRAD = 'd'; + $FORMAT_LONG = 'l'; + $FORMAT_COLUMN = 'c'; + $SORT_NAME = 'n'; + $SORT_SIZE = 's'; + $SORT_TIME = 't'; + local(@opt) = ($TYPE_TREE, $FORMAT_STANDRAD, $SORT_NAME); + local($_); + + open(CONFIG, "< $config") || return @opt; + while(<CONFIG>) { + chop; + s/^\s+//; + tr/A-Z/a-z/; + if (/^type\s+(\S)/i) { + $opt[$OPT_TYPE] = $1; + } elsif (/^format\s+(\S)/i) { + $opt[$OPT_FORMAT] = $1 + } elsif (/^sort\s+(\S)/i) { + $opt[$OPT_SORT] = $1; + } + } + close(CONFIG); + return @opt; +} + +sub update_option { + local($config) = @_; + + open(CONFIG, "> $config") || return; + print CONFIG <<EOF; +type $TYPE +format $FORMAT +sort $SORT +EOF + close(CONFIG); +} + +sub print_form { + local($d, @OPT) = @_; + local(@disc) = ('Type', 'Format', 'Sort'); + local(@val) = ( + "('t', 'd')", + "('s', 'd', 'c')", + "('n', 's', 't')", + ); + local(@opt) = ( + "('Tree', 'Standard')", + "('Short', 'Standard', 'Column')", + "('By Name', 'By Size', 'By Time')" + ); + local($_, @vs, @os, $v, $o); + + print <<EOF; +<form action=\"$CGI\"> +<center> +<table> +<tr valign=top> +EOF + foreach(0 .. 2) { + print "<td align> $disc[$_]</td>\n"; + } + print "</tr><tr>\n"; + foreach(0 .. 2) { + print "<td><select name=opt$_>\n"; + eval "\@vs = $val[$_]"; + eval "\@os = $opt[$_]"; + foreach $v (@vs) { + $o = shift(@os); + if ($v eq $OPT[$_]) { + print "<option value=$v selected>$o\n"; + } else { + print "<option value=$v>$o\n"; + } + } + print "</select></td>\n"; + } + print <<EOF; +<td><input type=submit name=cmd value="Update"></td> +</tr> +</table> +</center> +<input type=hidden name=dir value="$d"> +</form> +EOF +} + +sub html_quote { + local($_) = @_; + local(%QUOTE) = ( + '<', '<', + '>', '>', + '&', '&', + '"', '"', + ); + s/[<>&"]/$QUOTE{$&}/g; + return $_; +} +sub form_encode { + local($_) = @_; + s/[\t\r\n%#&=+]/sprintf('%%%2x', unpack('c', $&))/eg; + s/ /+/g; + return $_; +} + +sub form_decode { + local($_) = @_; + s/\+/ /g; + s/%([\da-f][\da-f])/pack('c', hex($1))/egi; + return $_; +} + +sub cleanup { + local($_) = @_; + + s@//+@/@g; + s@/\./@/@g; + while(m@/\.\./@) { + s@^/(\.\./)+@/@; + s@/[^/]+/\.\./@/@; + } + return $_; +} + +sub utype { + local($_) = @_; + local(%T) = ( + 0010000, 'PIPE', + 0020000, 'CHR', + 0040000, 'DIR', + 0060000, 'BLK', + 0100000, 'FILE', + 0120000, 'LINK', + 0140000, 'SOCK', + ); + return $T{($_ & 0170000)} || 'FILE'; +} + +sub umode { + local($_) = @_; + local(%T) = ( + 0010000, 'p', + 0020000, 'c', + 0040000, 'd', + 0060000, 'b', + 0100000, '-', + 0120000, 'l', + 0140000, 's', + ); + + return ($T{($_ & 0170000)} || '-') + . (($_ & 00400) ? 'r' : '-') + . (($_ & 00200) ? 'w' : '-') + . (($_ & 04000) ? 's' : + (($_ & 00100) ? 'x' : '-')) + . (($_ & 00040) ? 'r' : '-') + . (($_ & 00020) ? 'w' : '-') + . (($_ & 02000) ? 's' : + (($_ & 00010) ? 'x' : '-')) + . (($_ & 00004) ? 'r' : '-') + . (($_ & 00002) ? 'w' : '-') + . (($_ & 01000) ? 't' : + (($_ & 00001) ? 'x' : '-')); +} + +sub utime { + local($_) = @_; + local(@MON) = ( + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ); + local($sec,$min,$hour,$mday,$mon, + $year,$wday,$yday,$isdst) = localtime($_); + + if ($_ > $NOW - 182*24*60*60 && $_ < $NOW + 183*24*60*60) { + return sprintf("%3s %2d %.2d:%.2d", $MON[$mon], $mday, $hour, $min); + } else { + return sprintf("%3s %2d %5d", $MON[$mon], $mday, 1900+$year); + } +} + diff --git a/scrsize.c b/scrsize.c new file mode 100644 index 0000000..3c3c652 --- /dev/null +++ b/scrsize.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1999, NBG01720@nifty.ne.jp + * + * To compile this program: + * gcc -D__ST_MT_ERRNO__ -O2 -s -Zomf -Zmtd -lX11 scrsize.c + * + * When I wrote this routine, I consulted some part of the source code of the + * xwininfo utility by X Consortium. + * + * Copyright (c) 1987, X Consortium + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of the X Consortium shall not + * be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the X + * Consortium. + */ +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <stdlib.h> +#include <stdio.h> + +int main(){ + char*cp; + Display*dpy; + Window window; + XWindowAttributes win_attributes; + XSizeHints hints; + long longjunk; + int dst[2]; + + _scrsize(dst); + cp=getenv("WINDOWID"); + if(cp){ + dpy=XOpenDisplay(NULL); + if(dpy){ + if(XGetWindowAttributes(dpy,window=atol(cp),&win_attributes)) + if(XGetWMNormalHints(dpy,window,&hints,&longjunk)) + if(hints.flags&PResizeInc&&hints.width_inc&&hints.height_inc){ + if(hints.flags&(PBaseSize|PMinSize)){ + if(hints.flags&PBaseSize){ + win_attributes.width -=hints.base_width; + win_attributes.height-=hints.base_height; + }else{ + win_attributes.width -=hints.min_width; + win_attributes.height-=hints.min_height; + } + } + dst[0]=win_attributes.width /hints.width_inc; + dst[1]=win_attributes.height/hints.height_inc; + } + XCloseDisplay(dpy); + } + } + printf("%i %i\n",dst[0],dst[1]); + return 0; +} diff --git a/search.c b/search.c new file mode 100644 index 0000000..ae74284 --- /dev/null +++ b/search.c @@ -0,0 +1,147 @@ +#include "fm.h" +#include "regex.h" + +int +forwardSearch(Buffer * buf, char *str) +{ + char *p, *first, *last; + Line *l, *begin; + int wrapped = FALSE; + int pos; + + if ((p = regexCompile(str, IgnoreCase)) != NULL) { + message(p, 0, 0); + return FALSE; + } + l = begin = buf->currentLine; + pos = buf->pos + 1; +#ifdef JP_CHARSET + if (l->propBuf[pos] & PC_KANJI2) + pos++; +#endif + if (regexMatch(&l->lineBuf[pos], l->len - pos, 0) == 1) { + matchedPosition(&first, &last); + buf->pos = first - l->lineBuf; + arrangeCursor(buf); + return FALSE; + } + for (l = l->next;; l = l->next) { + if (l == NULL) { + if (buf->pagerSource) { + l = getNextPage(buf, 1); + if (l == NULL) { + if (WrapSearch && !wrapped) { + l = buf->firstLine; + wrapped = TRUE; + } + else { + break; + } + } + } + else if (WrapSearch) { + l = buf->firstLine; + wrapped = TRUE; + } + else { + break; + } + } + if (regexMatch(l->lineBuf, l->len, 1) == 1) { + matchedPosition(&first, &last); + if (wrapped && l == begin && buf->pos == first - l->lineBuf) + /* exactly same match */ + break; + buf->pos = first - l->lineBuf; + buf->currentLine = l; + gotoLine(buf, l->linenumber); + arrangeCursor(buf); + return wrapped; + } + if (wrapped && l == begin) /* no match */ + break; + } + disp_message("Not found", FALSE); + return FALSE; +} + +int +backwardSearch(Buffer * buf, char *str) +{ + char *p, *q, *found, *first, *last; + Line *l, *begin; + int wrapped = FALSE; + int pos; + + if ((p = regexCompile(str, IgnoreCase)) != NULL) { + message(p, 0, 0); + return FALSE; + } + l = begin = buf->currentLine; + if (buf->pos > 0) { + pos = buf->pos - 1; +#ifdef JP_CHARSET + if (l->propBuf[pos] & PC_KANJI2) + pos--; +#endif + p = &l->lineBuf[pos]; + found = NULL; + q = l->lineBuf; + while (regexMatch(q, &l->lineBuf[l->len] - q, q == l->lineBuf) == 1) { + matchedPosition(&first, &last); + if (first <= p) + found = first; +#ifdef JP_CHARSET + if (l->propBuf[q - l->lineBuf] & PC_KANJI1) + q += 2; + else +#endif + q++; + if (q > p) + break; + } + if (found) { + buf->pos = found - l->lineBuf; + arrangeCursor(buf); + return FALSE; + } + } + for (l = l->prev;; l = l->prev) { + if (l == NULL) { + if (WrapSearch) { + l = buf->lastLine; + wrapped = TRUE; + } + else { + break; + } + } + found = NULL; + q = l->lineBuf; + while (regexMatch(q, &l->lineBuf[l->len] - q, q == l->lineBuf) == 1) { + matchedPosition(&first, &last); + if (wrapped && l == begin && buf->pos == first - l->lineBuf) + /* exactly same match */ + ; + else + found = first; +#ifdef JP_CHARSET + if (l->propBuf[q - l->lineBuf] & PC_KANJI1) + q += 2; + else +#endif + q++; + } + if (found) { + buf->pos = found - l->lineBuf; + buf->currentLine = l; + gotoLine(buf, l->linenumber); + arrangeCursor(buf); + return wrapped; + } + if (wrapped && l == begin) /* no match */ + break; + } + disp_message("Not found", FALSE); + return FALSE; +} @@ -0,0 +1,3328 @@ +/* $Id: table.c,v 1.1 2001/11/08 05:15:40 a-ito Exp $ */ +/* + * HTML table + */ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#ifdef __EMX__ +#include <strings.h> +#endif /* __EMX__ */ + +#include "fm.h" +#include "html.h" +#include "parsetagx.h" +#include "Str.h" +#include "myctype.h" + +#ifdef KANJI_SYMBOLS +static char *rule[] = +{"��", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "}; +static char *ruleB[] = +{"00", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "}; +#define TN_VERTICALBAR "��" +#define HORIZONTALBAR "��" +#define RULE_WIDTH 2 +#else /* not KANJI_SYMBOLS */ +#if defined(__EMX__)&&!defined(JP_CHARSET) +extern int CodePage; + +static char *_rule[] = +#else +char alt_rule[] = { +'+', '|', '-', '+', '|', '|', '+', ' ', '-', '+', '-', ' ', '+', ' ', ' ', ' '}; +static char *rule[] = +#endif +{ + "<_RULE>\200</_RULE>", + "<_RULE>\201</_RULE>", + "<_RULE>\202</_RULE>", + "<_RULE>\203</_RULE>", + "<_RULE>\204</_RULE>", + "<_RULE>\205</_RULE>", + "<_RULE>\206</_RULE>", + "<_RULE>\207</_RULE>", + "<_RULE>\210</_RULE>", + "<_RULE>\211</_RULE>", + "<_RULE>\212</_RULE>", + "<_RULE>\213</_RULE>", + "<_RULE>\214</_RULE>", + "<_RULE>\215</_RULE>", + "<_RULE>\216</_RULE>", + "<_RULE>\217</_RULE>" +}; +#if defined(__EMX__)&&!defined(JP_CHARSET) +static char **ruleB = _rule, **rule = _rule; +static char *rule850[] = { + "\305", "\303", "\302", "\332", "\264", "\263" , "\277", "07", + "\301", "\300", "\304", "0B", "\331", "0D", "0E", " " }; +static char *ruleB850[] = { + "\316", "\314", "\313", "\311" "\271", "\272", "\273", "07", + "\312", "\310", "\315", "0B", "\274", "0D", "0E", " " }; +#else /* not __EMX__ or JP_CHARSET */ +static char **ruleB = rule; +#endif /* not __EMX__ or JP_CHARSET */ + +#define TN_VERTICALBAR "<_RULE>\205</_RULE>" +#define RULE_WIDTH 1 +#endif /* not KANJI_SYMBOLS */ + +#define RULE(mode) (((mode)==BORDER_THICK)?ruleB:rule) +#define TK_VERTICALBAR(mode) (RULE(mode)[5]) + +#define BORDERWIDTH 2 +#define BORDERHEIGHT 1 +#define NOBORDERWIDTH 1 +#define NOBORDERHEIGHT 0 + +#define HTT_X 1 +#define HTT_Y 2 +#define HTT_ALIGN 0x30 +#define HTT_LEFT 0x00 +#define HTT_CENTER 0x10 +#define HTT_RIGHT 0x20 +#define HTT_TRSET 0x40 +#define HTT_VALIGN 0x700 +#define HTT_TOP 0x100 +#define HTT_MIDDLE 0x200 +#define HTT_BOTTOM 0x400 +#define HTT_VTRSET 0x800 +#ifdef NOWRAP +#define HTT_NOWRAP 4 +#endif /* NOWRAP */ +#define TAG_IS(s,tag,len) (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len]))) + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif /* not max */ +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif /* not min */ +#ifndef abs +#define abs(a) ((a) >= 0. ? (a) : -(a)) +#endif /* not abs */ + +#ifdef MATRIX +#ifndef MESCHACH +#include "matrix.c" +#endif /* not MESCHACH */ +#endif /* MATRIX */ + +#ifdef MATRIX +int correct_table_matrix(struct table *, int, int, int, double); +void set_table_matrix(struct table *, int); +#endif /* MATRIX */ + +#ifdef MATRIX +static double +weight(int x) +{ + + if (x < COLS) + return (double) x; + else + return COLS * (log((double) x / COLS) + 1.); +} + +static double +weight2(int a) +{ + return (double) a / COLS * 4 + 1.; +} + +#define sigma_td(a) (0.5*weight2(a)) /* <td width=...> */ +#define sigma_td_nw(a) (32*weight2(a)) /* <td ...> */ +#define sigma_table(a) (0.25*weight2(a)) /* <table width=...> */ +#define sigma_table_nw(a) (2*weight2(a)) /* <table...> */ +#else /* not MATRIX */ +#define LOG_MIN 1.0 +static double +weight3(int x) +{ + if (x < 0.1) + return 0.1; + if (x < LOG_MIN) + return (double) x; + else + return LOG_MIN * (log((double) x / LOG_MIN) + 1.); +} +#endif /* not MATRIX */ + +static int +bsearch_2short(short e1, short *ent1, short e2, short *ent2, int base, + char *index, int nent) +{ + int n = nent; + int k = 0; + + int e = e1 * base + e2; + while (n > 0) { + int nn = n / 2; + int idx = index[k + nn]; + int ne = ent1[idx] * base + ent2[idx]; + if (ne == e) { + k += nn; + break; + } + else if (ne < e) { + n -= nn + 1; + k += nn + 1; + } + else { + n = nn; + } + } + return k; +} + +static int +bsearch_double(double e, double *ent, char *index, int nent) +{ + int n = nent; + int k = 0; + + while (n > 0) { + int nn = n / 2; + int idx = index[k + nn]; + double ne = ent[idx]; + if (ne == e) { + k += nn; + break; + } + else if (ne > e) { + n -= nn + 1; + k += nn + 1; + } + else { + n = nn; + } + } + return k; +} + +static int +ceil_at_intervals(int x, int step) +{ + int mo = x % step; + if (mo > 0) + x += step - mo; + else if (mo < 0) + x -= mo; + return x; +} + +static int +floor_at_intervals(int x, int step) +{ + int mo = x % step; + if (mo > 0) + x -= mo; + else if (mo < 0) + x += step - mo; + return x; +} + +#define round(x) ((int)floor((x)+0.5)) + +#ifndef MATRIX +static void +dv2sv(double *dv, short *iv, int size) +{ + int i, k, iw; + char *index; + double *edv; + double w = 0., x; + + index = NewAtom_N(char, size); + edv = NewAtom_N(double, size); + for (i = 0; i < size; i++) { + iv[i] = ceil(dv[i]); + edv[i] = (double) iv[i] - dv[i]; + } + + w = 0.; + for (k = 0; k < size; k++) { + x = edv[k]; + w += x; + i = bsearch_double(x, edv, index, k); + if (k > i) + bcopy(index + i, index + i + 1, k - i); + index[i] = k; + } + iw = min((int) (w + 0.5), size); + if (iw == 0) + return; + x = edv[(int) index[iw - 1]]; + for (i = 0; i < size; i++) { + k = index[i]; + if (i >= iw && abs(edv[k] - x) > 1e-6) + break; + iv[k]--; + } +} +#endif + +static int +table_colspan(struct table *t, int row, int col) +{ + int i; + for (i = col + 1; i <= t->maxcol && (t->tabattr[row][i] & HTT_X); i++); + return i - col; +} + +static int +table_rowspan(struct table *t, int row, int col) +{ + int i; + if (!t->tabattr[row]) + return 0; + for (i = row + 1; i <= t->maxrow && t->tabattr[i] && + (t->tabattr[i][col] & HTT_Y); i++); + return i - row; +} + +static int +minimum_cellspacing(int border_mode) +{ + switch (border_mode) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + return RULE_WIDTH; + case BORDER_NONE: + return 1; + default: + /* not reached */ + return 0; + } +} + +static int +table_border_width(struct table *t) +{ + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + return t->maxcol * t->cellspacing + 2 * (RULE_WIDTH + t->cellpadding); + case BORDER_NOWIN: + case BORDER_NONE: + return t->maxcol * t->cellspacing; + default: + /* not reached */ + return 0; + } +} + +struct table * +newTable() +{ + struct table *t; + int i, j; + + t = New(struct table); + t->max_rowsize = MAXROW; + t->tabdata = New_N(GeneralList **, MAXROW); + t->tabattr = New_N(table_attr *, MAXROW); + t->tabheight = NewAtom_N(short, MAXROW); +#ifdef ID_EXT + t->tabidvalue = New_N(Str *, MAXROW); + t->tridvalue = New_N(Str, MAXROW); +#endif /* ID_EXT */ + + for (i = 0; i < MAXROW; i++) { + t->tabdata[i] = NULL; + t->tabattr[i] = 0; + t->tabheight[i] = 0; +#ifdef ID_EXT + t->tabidvalue[i] = NULL; + t->tridvalue[i] = NULL; +#endif /* ID_EXT */ + } + for (j = 0; j < MAXCOL; j++) { + t->tabwidth[j] = 0; + t->minimum_width[j] = 0; + t->fixed_width[j] = 0; + } + t->cell.maxcell = -1; + t->cell.icell = -1; + t->ntable = 0; + t->tables_size = 0; + t->tables = NULL; +#ifdef MATRIX + t->matrix = NULL; + t->vector = NULL; +#endif /* MATRIX */ +#if 0 + t->tabcontentssize = 0; + t->indent = 0; + t->linfo.prev_ctype = PC_ASCII; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; +#endif + t->trattr = 0; + + t->caption = Strnew(); + t->suspended_data = NULL; +#ifdef ID_EXT + t->id = NULL; +#endif + return t; +} + +static void +check_row(struct table *t, int row) +{ + int i, r; + GeneralList ***tabdata; + table_attr **tabattr; + short *tabheight; +#ifdef ID_EXT + Str **tabidvalue; + Str *tridvalue; +#endif /* ID_EXT */ + + if (row >= t->max_rowsize) { + r = max(t->max_rowsize * 2, row + 1); + tabdata = New_N(GeneralList **, r); + tabattr = New_N(table_attr *, r); + tabheight = New_N(short, r); +#ifdef ID_EXT + tabidvalue = New_N(Str *, r); + tridvalue = New_N(Str, r); +#endif /* ID_EXT */ + for (i = 0; i < t->max_rowsize; i++) { + tabdata[i] = t->tabdata[i]; + tabattr[i] = t->tabattr[i]; + tabheight[i] = t->tabheight[i]; +#ifdef ID_EXT + tabidvalue[i] = t->tabidvalue[i]; + tridvalue[i] = t->tridvalue[i]; +#endif /* ID_EXT */ + } + for (; i < r; i++) { + tabdata[i] = NULL; + tabattr[i] = NULL; + tabheight[i] = 0; +#ifdef ID_EXT + tabidvalue[i] = NULL; + tridvalue[i] = NULL; +#endif /* ID_EXT */ + } + t->tabdata = tabdata; + t->tabattr = tabattr; + t->tabheight = tabheight; +#ifdef ID_EXT + t->tabidvalue = tabidvalue; + t->tridvalue = tridvalue; +#endif /* ID_EXT */ + t->max_rowsize = r; + } + + if (t->tabdata[row] == NULL) { + t->tabdata[row] = New_N(GeneralList *, MAXCOL); + t->tabattr[row] = NewAtom_N(table_attr, MAXCOL); +#ifdef ID_EXT + t->tabidvalue[row] = New_N(Str, MAXCOL); +#endif /* ID_EXT */ + for (i = 0; i < MAXCOL; i++) { + t->tabdata[row][i] = NULL; + t->tabattr[row][i] = 0; +#ifdef ID_EXT + t->tabidvalue[row][i] = NULL; +#endif /* ID_EXT */ + } + } +} + +void +pushdata(struct table *t, int row, int col, char *data) +{ + check_row(t, row); + if (t->tabdata[row][col] == NULL) + t->tabdata[row][col] = newGeneralList(); + + pushText(t->tabdata[row][col], data); +} + +void +suspend_or_pushdata(struct table *tbl, char *line) +{ + if (tbl->flag & TBL_IN_COL) + pushdata(tbl, tbl->row, tbl->col, line); + else { + if (!tbl->suspended_data) + tbl->suspended_data = newTextList(); + pushText(tbl->suspended_data, line); + } +} + +int visible_length_offset = 0; +int +visible_length(char *str) +{ + int len = 0; + int status = R_ST_NORMAL; + int prev_status = status; + Str tagbuf = Strnew(); + char *t, *r2; + int amp_len; + + t = str; + while (*str) { + prev_status = status; + len += next_status(*str, &status); + if (status == R_ST_TAG0) { + Strclear(tagbuf); + Strcat_char(tagbuf, *str); + } + else if (status == R_ST_TAG || status == R_ST_DQUOTE || status == R_ST_QUOTE || status == R_ST_EQL) { + Strcat_char(tagbuf, *str); + } + else if (status == R_ST_AMP) { + if (prev_status == R_ST_NORMAL) { + Strclear(tagbuf); + amp_len = 0; + } + else { + Strcat_char(tagbuf, *str); + len++; + amp_len++; + } + } + else if (status == R_ST_NORMAL && prev_status == R_ST_AMP) { + Strcat_char(tagbuf, *str); + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + len += strlen(t) - 1 - amp_len; + if (*r2 != '\0') { + str -= strlen(r2); + } + } + else if (status == R_ST_NORMAL && ST_IS_REAL_TAG(prev_status)) { + ; + } + else if (*str == '\t') { + len--; + do { + len++; + } while ((visible_length_offset + len) % Tabstop != 0); + } + str++; + } + if (status == R_ST_AMP) { + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + len += strlen(t) - 1 - amp_len; + if (*r2 != '\0') { + len += strlen(r2); + } + } + return len; +} + +int +maximum_visible_length(char *str) +{ + int maxlen, len; + char *p; + + for (p = str; *p && *p != '\t'; p++); + + visible_length_offset = 0; + maxlen = visible_length(str); + + if (*p == '\0') + return maxlen; + + for (visible_length_offset = 1; visible_length_offset < Tabstop; + visible_length_offset++) { + len = visible_length(str); + if (maxlen < len) { + maxlen = len; + break; + } + } + return maxlen; +} + +void +align(TextLine *lbuf, int width, int mode) +{ + int i, l, l1, l2; + Str buf, line = lbuf->line; + + if (line->length == 0) { + for (i = 0; i < width; i++) + Strcat_char(line, ' '); + lbuf->pos = width; + return; + } + buf = Strnew(); + l = width - lbuf->pos; + switch (mode) { + case ALIGN_CENTER: + l1 = l / 2; + l2 = l - l1; + for (i = 0; i < l1; i++) + Strcat_char(buf, ' '); + Strcat(buf, line); + for (i = 0; i < l2; i++) + Strcat_char(buf, ' '); + break; + case ALIGN_LEFT: + Strcat(buf, line); + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + break; + case ALIGN_RIGHT: + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + Strcat(buf, line); + break; + default: + return; + } + lbuf->line = buf; + if (lbuf->pos < width) + lbuf->pos = width; +} + +void +print_item(struct table *t, + int row, int col, int width, + Str buf) +{ + int alignment; + TextLine *lbuf; + + if (t->tabdata[row]) + lbuf = popTextLine(t->tabdata[row][col]); + else + lbuf = NULL; + + if (lbuf != NULL) { + check_row(t, row); + alignment = ALIGN_CENTER; + if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_LEFT) + alignment = ALIGN_LEFT; + else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_RIGHT) + alignment = ALIGN_RIGHT; + else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_CENTER) + alignment = ALIGN_CENTER; + align(lbuf, width, alignment); + Strcat(buf, lbuf->line); + } + else { + lbuf = newTextLine(NULL, 0); + align(lbuf, width, ALIGN_CENTER); + Strcat(buf, lbuf->line); + } +} + + +#define T_TOP 0 +#define T_MIDDLE 1 +#define T_BOTTOM 2 + +void +print_sep(struct table *t, + int row, int type, int maxcol, + Str buf) +{ + int forbid; + char **rulep; + int i, j, k, l, m; + +#if defined(__EMX__)&&!defined(JP_CHARSET) + if(CodePage==850){ + ruleB = ruleB850; + rule = rule850; + } +#endif + if (row >= 0) + check_row(t, row); + check_row(t, row + 1); + if ((type == T_TOP || type == T_BOTTOM) && t->border_mode == BORDER_THICK) { + rulep = ruleB; + } + else { + rulep = rule; + } + forbid = 1; + if (type == T_TOP) + forbid |= 2; + else if (type == T_BOTTOM) + forbid |= 8; + else if (t->tabattr[row + 1][0] & HTT_Y) { + forbid |= 4; + } + if (t->border_mode != BORDER_NOWIN) + Strcat_charp(buf, RULE(t->border_mode)[forbid]); + for (i = 0; i <= maxcol; i++) { + forbid = 10; + if (type != T_BOTTOM && (t->tabattr[row + 1][i] & HTT_Y)) { + if (t->tabattr[row + 1][i] & HTT_X) { + goto do_last_sep; + } + else { + for (k = row; k >= 0 && t->tabattr[k] && (t->tabattr[k][i] & HTT_Y); k--); + m = t->tabwidth[i] + 2 * t->cellpadding; + for (l = i + 1; l <= t->maxcol && (t->tabattr[row][l] & HTT_X); l++) + m += t->tabwidth[l] + t->cellspacing; + print_item(t, k, i, m, buf); + } + } + else { + for (j = 0; j < t->tabwidth[i] + 2 * t->cellpadding; j += RULE_WIDTH) { + Strcat_charp(buf, rulep[forbid]); + } + } + do_last_sep: + if (i < maxcol) { + forbid = 0; + if (type == T_TOP) + forbid |= 2; + else if (t->tabattr[row][i + 1] & HTT_X) { + forbid |= 2; + } + if (type == T_BOTTOM) + forbid |= 8; + else { + if (t->tabattr[row + 1][i + 1] & HTT_X) { + forbid |= 8; + } + if (t->tabattr[row + 1][i + 1] & HTT_Y) { + forbid |= 4; + } + if (t->tabattr[row + 1][i] & HTT_Y) { + forbid |= 1; + } + } + if (forbid != 15) /* forbid==15 means 'no rule at all' */ + Strcat_charp(buf, rulep[forbid]); + } + } + forbid = 4; + if (type == T_TOP) + forbid |= 2; + if (type == T_BOTTOM) + forbid |= 8; + if (t->tabattr[row + 1][maxcol] & HTT_Y) { + forbid |= 1; + } + if (t->border_mode != BORDER_NOWIN) + Strcat_charp(buf, RULE(t->border_mode)[forbid]); +} + +static int +get_spec_cell_width(struct table *tbl, int row, int col) +{ + int i, w; + + w = tbl->tabwidth[col]; + for (i = col + 1; i <= tbl->maxcol; i++) { + check_row(tbl, row); + if (tbl->tabattr[row][i] & HTT_X) + w += tbl->tabwidth[i] + tbl->cellspacing; + else + break; + } + return w; +} + +void +do_refill(struct table *tbl, int row, int col, int maxlimit) +{ + TextList *orgdata; + TextListItem *l; + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + int colspan, icell; + + if (tbl->tabdata[row] == NULL || + tbl->tabdata[row][col] == NULL) + return; + orgdata = (TextList *)tbl->tabdata[row][col]; + tbl->tabdata[row][col] = newGeneralList(); + + init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, + (TextLineList *)tbl->tabdata[row][col], + get_spec_cell_width(tbl, row, col), 0); + if (h_env.limit > maxlimit) + h_env.limit = maxlimit; + if (tbl->border_mode != BORDER_NONE && tbl->vcellpadding > 0) + do_blankline(&h_env, &obuf, 0, 0, h_env.limit); + for (l = orgdata->first; l != NULL; l = l->next) { + if (TAG_IS(l->ptr, "<table_alt", 10)) { + int id = -1; + char *p = l->ptr; + struct parsed_tag *tag; + if ((tag = parse_tag(&p, TRUE)) != NULL) + parsedtag_get_value(tag, ATTR_TID, &id); + if (id >= 0 && id < tbl->ntable) { + int alignment; + TextLineListItem *ti; + struct table *t = tbl->tables[id].ptr; + int limit = tbl->tables[id].indent + t->total_width; + tbl->tables[id].ptr = NULL; + save_fonteffect(&h_env, h_env.obuf); + flushline(&h_env, &obuf, 0, 0, h_env.limit); + if (t->vspace > 0 && !(obuf.flag & RB_IGNORE_P)) + do_blankline(&h_env, &obuf, 0, 0, h_env.limit); + if (RB_GET_ALIGN(h_env.obuf) == RB_CENTER) + alignment = ALIGN_CENTER; + else if (RB_GET_ALIGN(h_env.obuf) == RB_RIGHT) + alignment = ALIGN_RIGHT; + else + alignment = ALIGN_LEFT; + + if (alignment != ALIGN_LEFT) { + for (ti = tbl->tables[id].buf->first; + ti != NULL; + ti = ti->next) + align(ti->ptr, h_env.limit, alignment); + } + appendTextLineList(h_env.buf, tbl->tables[id].buf); + if (h_env.maxlimit < limit) + h_env.maxlimit = limit; + restore_fonteffect(&h_env, h_env.obuf); + obuf.flag &= ~RB_IGNORE_P; + h_env.blank_lines = 0; + if (t->vspace > 0) { + do_blankline(&h_env, &obuf, 0, 0, h_env.limit); + obuf.flag |= RB_IGNORE_P; + } + } + } + else + HTMLlineproc1(l->ptr, &h_env); + } + completeHTMLstream(&h_env, &obuf); + flushline(&h_env, &obuf, 0, 2, h_env.limit); + if (tbl->border_mode == BORDER_NONE) { + int rowspan = table_rowspan(tbl, row, col); + if (row + rowspan <= tbl->maxrow) { + if (tbl->vcellpadding > 0 && !(obuf.flag & RB_IGNORE_P)) + do_blankline(&h_env, &obuf, 0, 0, h_env.limit); + } + else { + if (tbl->vspace > 0) + purgeline(&h_env); + } + } + else { + if (tbl->vcellpadding > 0) { + if (!(obuf.flag & RB_IGNORE_P)) + do_blankline(&h_env, &obuf, 0, 0, h_env.limit); + } + else + purgeline(&h_env); + } + if ((colspan = table_colspan(tbl, row, col)) > 1) { + struct table_cell *cell = &tbl->cell; + int k; + k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL, + cell->index, cell->maxcell + 1); + icell = cell->index[k]; + if (cell->minimum_width[icell] < h_env.maxlimit) + cell->minimum_width[icell] = h_env.maxlimit; + } + else { + if (tbl->minimum_width[col] < h_env.maxlimit) + tbl->minimum_width[col] = h_env.maxlimit; + } +} + +static int +table_rule_width(struct table *t) +{ + if (t->border_mode == BORDER_NONE) + return 1; + return RULE_WIDTH; +} + +static void +check_cell_width(short *tabwidth, short *cellwidth, + short *col, short *colspan, short maxcell, + char *index, int space, int dir) +{ + int i, j, k, bcol, ecol; + int swidth, width; + + for (k = 0; k <= maxcell; k++) { + j = index[k]; + if (cellwidth[j] <= 0) + continue; + bcol = col[j]; + ecol = bcol + colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) + swidth += tabwidth[i]; + + width = cellwidth[j] - (colspan[j] - 1) * space; + if (width > swidth) { + int w = (width - swidth) / colspan[j]; + int r = (width - swidth) % colspan[j]; + for (i = bcol; i < ecol; i++) + tabwidth[i] += w; + /* dir {0: horizontal, 1: vertical} */ + if (dir == 1 && r > 0) + r = colspan[j]; + for (i = 1; i <= r; i++) + tabwidth[ecol - i]++; + } + } +} + +void +check_minimum_width(struct table *t, short *tabwidth) +{ + int i; + struct table_cell *cell = &t->cell; + + for (i = 0; i <= t->maxcol; i++) { + if (tabwidth[i] < t->minimum_width[i]) + tabwidth[i] = t->minimum_width[i]; + } + + check_cell_width(tabwidth, cell->minimum_width, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); +} + +void +check_maximum_width(struct table *t) +{ + struct table_cell *cell = &t->cell; +#ifdef MATRIX + int i, j, bcol, ecol; + int swidth, width; + + cell->necell = 0; + for (j = 0; j <= cell->maxcell; j++) { + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) + swidth += t->tabwidth[i]; + + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + if (width > swidth) { + cell->eindex[cell->necell] = j; + cell->necell++; + } + } +#else /* not MATRIX */ + check_cell_width(t->tabwidth, cell->width, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + check_minimum_width(t, t->tabwidth); +#endif /* not MATRIX */ +} + + +#ifdef MATRIX +static void +set_integered_width(struct table *t, double *dwidth, short *iwidth) +{ + int i, j, k, n, bcol, ecol, step; + char *index, *fixed; + double *mod; + double sum = 0., x; + struct table_cell *cell = &t->cell; + int rulewidth = table_rule_width(t); + + index = NewAtom_N(char, t->maxcol + 1); + mod = NewAtom_N(double, t->maxcol + 1); + for (i = 0; i <= t->maxcol; i++) { + iwidth[i] = ceil_at_intervals(ceil(dwidth[i]), rulewidth); + mod[i] = (double) iwidth[i] - dwidth[i]; + } + + sum = 0.; + for (k = 0; k <= t->maxcol; k++) { + x = mod[k]; + sum += x; + i = bsearch_double(x, mod, index, k); + if (k > i) + bcopy(index + i, index + i + 1, k - i); + index[i] = k; + } + + fixed = NewAtom_N(char, t->maxcol + 1); + bzero(fixed, t->maxcol + 1); + for (step = 0; step < 2; step++) { + for (i = 0; i <= t->maxcol; i += n) { + int nn; + char *idx; + double nsum; + if (sum < 0.5) + return; + for (n = 0; i + n <= t->maxcol; n++) { + int ii = index[i + n]; + if (n == 0) + x = mod[ii]; + else if (fabs(mod[ii] - x) > 1e-6) + break; + } + for (k = 0; k < n; k++) { + int ii = index[i + k]; + if (fixed[ii] < 2 && + iwidth[ii] - rulewidth < t->minimum_width[ii]) + fixed[ii] = 2; + if (fixed[ii] < 1 && + iwidth[ii] - rulewidth < t->tabwidth[ii] && + (double) rulewidth - mod[ii] > 0.5) + fixed[ii] = 1; + } + idx = NewAtom_N(char, n); + for (k = 0; k < cell->maxcell; k++) { + int kk, w, width, m; + j = cell->index[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + m = 0; + for (kk = 0; kk < n; kk++) { + int ii = index[i + kk]; + if (ii >= bcol && ii < ecol) { + idx[m] = ii; + m++; + } + } + if (m == 0) + continue; + width = (cell->colspan[j] - 1) * t->cellspacing; + for (kk = bcol; kk < ecol; kk++) + width += iwidth[kk]; + w = 0; + for (kk = 0; kk < m; kk++) { + if (fixed[(int) idx[kk]] < 2) + w += rulewidth; + } + if (width - w < cell->minimum_width[j]) { + for (kk = 0; kk < m; kk++) { + if (fixed[(int) idx[kk]] < 2) + fixed[(int) idx[kk]] = 2; + } + } + w = 0; + for (kk = 0; kk < m; kk++) { + if (fixed[(int) idx[kk]] < 1 && + (double) rulewidth - mod[(int) idx[kk]] > 0.5) + w += rulewidth; + } + if (width - w < cell->width[j]) { + for (kk = 0; kk < m; kk++) { + if (fixed[(int) idx[kk]] < 1 && + (double) rulewidth - mod[(int) idx[kk]] > 0.5) + fixed[(int) idx[kk]] = 1; + } + } + } + nn = 0; + for (k = 0; k < n; k++) { + int ii = index[i + k]; + if (fixed[ii] <= step) + nn++; + } + nsum = sum - (double) (nn * rulewidth); + if (nsum < 0. && fabs(sum) <= fabs(nsum)) + return; + for (k = 0; k < n; k++) { + int ii = index[i + k]; + if (fixed[ii] <= step) { + iwidth[ii] -= rulewidth; + fixed[ii] = 3; + } + } + sum = nsum; + } + } +} + +static double +correlation_coefficient(double sxx, double syy, double sxy) +{ + double coe, tmp; + tmp = sxx * syy; + if (tmp < Tiny) + tmp = Tiny; + coe = sxy / sqrt(tmp); + if (coe > 1.) + return 1.; + if (coe < -1.) + return -1.; + return coe; +} + +static double +correlation_coefficient2(double sxx, double syy, double sxy) +{ + double coe, tmp; + tmp = (syy + sxx - 2*sxy) * sxx; + if (tmp < Tiny) + tmp = Tiny; + coe = (sxx - sxy) / sqrt(tmp); + if (coe > 1.) + return 1.; + if (coe < -1.) + return -1.; + return coe; +} + +static double +recalc_width(double old, double swidth, int cwidth, + double sxx, double syy, double sxy, + int is_inclusive) +{ + double delta = swidth - (double) cwidth; + double rat = sxy / sxx, + coe = correlation_coefficient(sxx, syy, sxy), + w, ww; + if (old < 0.) + old = 0.; + if (fabs(coe) < 1e-5) + return old; + w = rat * old; + ww = delta; + if (w > 0.) { + double wmin = 5e-3 * sqrt(syy * (1. - coe * coe)); + if (swidth < 0.2 && cwidth > 0 && is_inclusive) { + double coe1 = correlation_coefficient2(sxx, syy, sxy); + if (coe > 0.9 || coe1 > 0.9) + return 0.; + } + if (wmin > 0.05) + wmin = 0.05; + if (ww < 0.) + ww = 0.; + ww += wmin; + } + else { + double wmin = 5e-3 * sqrt(syy) * fabs(coe); + if (rat > -0.001) + return old; + if (wmin > 0.01) + wmin = 0.01; + if (ww > 0.) + ww = 0.; + ww -= wmin; + } + if (w > ww) + return ww / rat; + return old; +} + +static int +check_compressible_cell(struct table *t, MAT * minv, + double *newwidth, double *swidth, short *cwidth, + double totalwidth, double *Sxx, + int icol, int icell, double sxx, int corr) +{ + struct table_cell *cell = &t->cell; + int i, j, k, m, bcol, ecol, span; + double delta, owidth; + double dmax, dmin, sxy; + int rulewidth = table_rule_width(t); + + if (sxx < 10.) + return corr; + + if (icol >= 0) { + owidth = newwidth[icol]; + delta = newwidth[icol] - (double) t->tabwidth[icol]; + bcol = icol; + ecol = bcol + 1; + } + else if (icell >= 0) { + owidth = swidth[icell]; + delta = swidth[icell] - (double) cwidth[icell]; + bcol = cell->col[icell]; + ecol = bcol + cell->colspan[icell]; + } + else { + owidth = totalwidth; + delta = totalwidth; + bcol = 0; + ecol = t->maxcol + 1; + } + + dmin = delta; + dmax = -1.; + for (k = 0; k <= cell->maxcell; k++) { + int bcol1, ecol1; + int is_inclusive = 0; + if (dmin <= 0.) + goto _end; + j = cell->index[k]; + if (j == icell) + continue; + bcol1 = cell->col[j]; + ecol1 = bcol1 + cell->colspan[j]; + sxy = 0.; + for (m = bcol1; m < ecol1; m++) { + for (i = bcol; i < ecol; i++) + sxy += m_entry(minv, i, m); + } + if (bcol1 >= bcol && ecol1 <= ecol) { + is_inclusive = 1; + } + if (sxy > 0.) + dmin = recalc_width(dmin, swidth[j], cwidth[j], + sxx, Sxx[j], sxy, + is_inclusive); + else + dmax = recalc_width(dmax, swidth[j], cwidth[j], + sxx, Sxx[j], sxy, + is_inclusive); + } + for (m = 0; m <= t->maxcol; m++) { + int is_inclusive = 0; + if (dmin <= 0.) + goto _end; + if (m == icol) + continue; + sxy = 0.; + for (i = bcol; i < ecol; i++) + sxy += m_entry(minv, i, m); + if (m >= bcol && m < ecol) { + is_inclusive = 1; + } + if (sxy > 0.) + dmin = recalc_width(dmin, newwidth[m], t->tabwidth[m], + sxx, m_entry(minv, m, m), sxy, + is_inclusive); + else + dmax = recalc_width(dmax, newwidth[m], t->tabwidth[m], + sxx, m_entry(minv, m, m), sxy, + is_inclusive); + } + _end: + if (dmax > 0. && dmin > dmax) + dmin = dmax; + span = ecol - bcol; + if ((span == t->maxcol + 1 && dmin >= 0.) || + (span != t->maxcol + 1 && dmin > rulewidth * 0.5)) { + int nwidth = ceil_at_intervals(round(owidth - dmin), rulewidth); + correct_table_matrix(t, bcol, ecol - bcol, nwidth, 1.); + corr++; + } + return corr; +} + +#define MAX_ITERATION 10 +int +check_table_width(struct table *t, double *newwidth, MAT * minv, int itr) +{ + int i, j, k, m, bcol, ecol; + int corr = 0; + struct table_cell *cell = &t->cell; +#ifdef __GNUC__ + short orgwidth[t->maxcol + 1], corwidth[t->maxcol + 1]; + short cwidth[cell->maxcell + 1]; + double swidth[cell->maxcell + 1]; +#else /* __GNUC__ */ + short orgwidth[MAXCOL], corwidth[MAXCOL]; + short cwidth[MAXCELL]; + double swidth[MAXCELL]; +#endif /* __GNUC__ */ + double twidth, sxy, *Sxx, stotal; + + twidth = 0.; + stotal = 0.; + for (i = 0; i <= t->maxcol; i++) { + twidth += newwidth[i]; + stotal += m_entry(minv, i, i); + for (m = 0; m < i; m++) { + stotal += 2 * m_entry(minv, i, m); + } + } + + Sxx = NewAtom_N(double, cell->maxcell + 1); + for (k = 0; k <= cell->maxcell; k++) { + j = cell->index[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth[j] = 0.; + for (i = bcol; i < ecol; i++) + swidth[j] += newwidth[i]; + cwidth[j] = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + Sxx[j] = 0.; + for (i = bcol; i < ecol; i++) { + Sxx[j] += m_entry(minv, i, i); + for (m = bcol; m <= ecol; m++) { + if (m < i) + Sxx[j] += 2 * m_entry(minv, i, m); + } + } + } + + /* compress table */ + corr = check_compressible_cell(t, minv, newwidth, swidth, + cwidth, twidth, Sxx, + -1, -1, stotal, corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + + /* compress multicolumn cell */ + for (k = cell->maxcell; k >= 0; k--) { + j = cell->index[k]; + corr = check_compressible_cell(t, minv, newwidth, swidth, + cwidth, twidth, Sxx, + -1, j, Sxx[j], corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + } + + /* compress single column cell */ + for (i = 0; i <= t->maxcol; i++) { + corr = check_compressible_cell(t, minv, newwidth, swidth, + cwidth, twidth, Sxx, + i, -1, m_entry(minv, i, i), corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + } + + + for (i = 0; i <= t->maxcol; i++) + corwidth[i] = orgwidth[i] = round(newwidth[i]); + + check_minimum_width(t, corwidth); + + for (i = 0; i <= t->maxcol; i++) { + double sx = sqrt(m_entry(minv, i, i)); + if (sx < 0.1) + continue; + if (orgwidth[i] < t->minimum_width[i] && + corwidth[i] == t->minimum_width[i]) { + double w = (sx > 0.5) ? 0.5 : sx * 0.2; + sxy = 0.; + for (m = 0; m <= t->maxcol; m++) { + if (m == i) + continue; + sxy += m_entry(minv, i, m); + } + if (sxy <= 0.) { + correct_table_matrix(t, i, 1, t->minimum_width[i], w); + corr++; + } + } + } + + for (k = 0; k <= cell->maxcell; k++) { + int nwidth = 0, mwidth; + double sx; + + j = cell->index[k]; + sx = sqrt(Sxx[j]); + if (sx < 0.1) + continue; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + for (i = bcol; i < ecol; i++) + nwidth += corwidth[i]; + mwidth = cell->minimum_width[j] - (cell->colspan[j] - 1) * t->cellspacing; + if (mwidth > swidth[j] && mwidth == nwidth) { + double w = (sx > 0.5) ? 0.5 : sx * 0.2; + + sxy = 0.; + for (i = bcol; i < ecol; i++) { + for (m = 0; m <= t->maxcol; m++) { + if (m >= bcol && m < ecol) + continue; + sxy += m_entry(minv, i, m); + } + } + if (sxy <= 0.) { + correct_table_matrix(t, bcol, cell->colspan[j], mwidth, w); + corr++; + } + } + } + + if (itr >= MAX_ITERATION) + return 0; + else + return corr; +} + +#else /* not MATRIX */ +void +set_table_width(struct table *t, short *newwidth, int maxwidth) +{ + int i, j, k, bcol, ecol; + struct table_cell *cell = &t->cell; + char *fixed; + int swidth, fwidth, width, nvar; + double s; + double *dwidth; + int try_again; + + fixed = NewAtom_N(char, t->maxcol + 1); + bzero(fixed, t->maxcol + 1); + dwidth = NewAtom_N(double, t->maxcol + 1); + + for (i = 0; i <= t->maxcol; i++) { + dwidth[i] = 0.0; + if (t->fixed_width[i] < 0) { + t->fixed_width[i] = -t->fixed_width[i] * maxwidth / 100; + } + if (t->fixed_width[i] > 0) { + newwidth[i] = t->fixed_width[i]; + fixed[i] = 1; + } + else + newwidth[i] = 0; + if (newwidth[i] < t->minimum_width[i]) + newwidth[i] = t->minimum_width[i]; + } + + for (k = 0; k <= cell->maxcell; k++) { + j = cell->index[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + + if (cell->fixed_width[j] < 0) + cell->fixed_width[j] = -cell->fixed_width[j] * maxwidth / 100; + + swidth = 0; + fwidth = 0; + nvar = 0; + for (i = bcol; i < ecol; i++) { + if (fixed[i]) { + fwidth += newwidth[i]; + } + else { + swidth += newwidth[i]; + nvar++; + } + } + width = max(cell->fixed_width[j], cell->minimum_width[j]) + - (cell->colspan[j] - 1) * t->cellspacing; + if (nvar > 0 && width > fwidth + swidth) { + s = 0.; + for (i = bcol; i < ecol; i++) { + if (!fixed[i]) + s += weight3(t->tabwidth[i]); + } + for (i = bcol; i < ecol; i++) { + if (!fixed[i]) + dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s; + else + dwidth[i] = (double) newwidth[i]; + } + dv2sv(dwidth, newwidth, cell->colspan[j]); + if (cell->fixed_width[j] > 0) { + for (i = bcol; i < ecol; i++) + fixed[i] = 1; + } + } + } + + do { + nvar = 0; + swidth = 0; + fwidth = 0; + for (i = 0; i <= t->maxcol; i++) { + if (fixed[i]) { + fwidth += newwidth[i]; + } + else { + swidth += newwidth[i]; + nvar++; + } + } + width = maxwidth - t->maxcol * t->cellspacing; + if (nvar == 0 || width <= fwidth + swidth) + break; + + s = 0.; + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) + s += weight3(t->tabwidth[i]); + } + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) + dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s; + else + dwidth[i] = (double) newwidth[i]; + } + dv2sv(dwidth, newwidth, t->maxcol + 1); + + try_again = 0; + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) { + if (newwidth[i] > t->tabwidth[i]) { + newwidth[i] = t->tabwidth[i]; + fixed[i] = 1; + try_again = 1; + } + else if (newwidth[i] < t->minimum_width[i]) { + newwidth[i] = t->minimum_width[i]; + fixed[i] = 1; + try_again = 1; + } + } + } + } while (try_again); +} +#endif /* not MATRIX */ + +void +check_table_height(struct table *t) +{ + int i, j, k; + struct { + short row[MAXCELL]; + short rowspan[MAXCELL]; + char index[MAXCELL]; + short maxcell; + short height[MAXCELL]; + } cell; + int space; + + cell.maxcell = -1; + + for (j = 0; j <= t->maxrow; j++) { + if (!t->tabattr[j]) + continue; + for (i = 0; i <= t->maxcol; i++) { + int t_dep, rowspan; + if (t->tabattr[j][i] & (HTT_X | HTT_Y)) + continue; + + if (t->tabdata[j][i] == NULL) + t_dep = 0; + else + t_dep = t->tabdata[j][i]->nitem; + + rowspan = table_rowspan(t, j, i); + if (rowspan > 1) { + int c = cell.maxcell + 1; + k = bsearch_2short(rowspan, cell.rowspan, + j, cell.row, t->maxrow + 1, + cell.index, c); + if (k <= cell.maxcell) { + int idx = cell.index[k]; + if (cell.row[idx] == j && + cell.rowspan[idx] == rowspan) + c = idx; + } + if (c > cell.maxcell && c < MAXCELL) { + cell.maxcell++; + cell.row[cell.maxcell] = j; + cell.rowspan[cell.maxcell] = rowspan; + cell.height[cell.maxcell] = 0; + if (cell.maxcell > k) + bcopy(cell.index + k, cell.index + k + 1, cell.maxcell - k); + cell.index[k] = cell.maxcell; + } + if (c <= cell.maxcell && c >= 0 && + cell.height[c] < t_dep) { + cell.height[c] = t_dep; + continue; + } + } + if (t->tabheight[j] < t_dep) + t->tabheight[j] = t_dep; + } + } + + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + space = 1; + break; + case BORDER_NONE: + space = 0; + } + check_cell_width(t->tabheight, cell.height, cell.row, cell.rowspan, + cell.maxcell, cell.index, space, 1); +} + +#define CHECK_MINIMUM 1 +#define CHECK_FIXED 2 + +int +get_table_width(struct table *t, short *orgwidth, short *cellwidth, + int flag) +{ +#ifdef __GNUC__ + short newwidth[t->maxcol + 1]; +#else /* not __GNUC__ */ + short newwidth[MAXCOL]; +#endif /* not __GNUC__ */ + int i; + int swidth; + struct table_cell *cell = &t->cell; + int rulewidth = table_rule_width(t); + + for (i = 0; i <= t->maxcol; i++) + newwidth[i] = max(orgwidth[i], 0); + + if (flag & CHECK_FIXED) { +#ifdef __GNUC__ + short ccellwidth[cell->maxcell + 1]; +#else /* not __GNUC__ */ + short ccellwidth[MAXCELL]; +#endif /* not __GNUC__ */ + for (i = 0; i <= t->maxcol; i++) { + if (newwidth[i] < t->fixed_width[i]) + newwidth[i] = t->fixed_width[i]; + } + for (i = 0; i <= cell->maxcell; i++) { + ccellwidth[i] = cellwidth[i]; + if (ccellwidth[i] < cell->fixed_width[i]) + ccellwidth[i] = cell->fixed_width[i]; + } + check_cell_width(newwidth, ccellwidth, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + } + else { + check_cell_width(newwidth, cellwidth, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + } + if (flag & CHECK_MINIMUM) + check_minimum_width(t, newwidth); + + swidth = 0; + for (i = 0; i <= t->maxcol; i++) { + swidth += ceil_at_intervals(newwidth[i], rulewidth); + } + swidth += table_border_width(t); + return swidth; +} + +#define minimum_table_width(t)\ +(get_table_width(t,t->minimum_width,t->cell.minimum_width,0)) +#define maximum_table_width(t)\ + (get_table_width(t,t->tabwidth,t->cell.width,CHECK_FIXED)) +#define fixed_table_width(t)\ + (get_table_width(t,t->fixed_width,t->cell.fixed_width,CHECK_MINIMUM)) + +void +renderCoTable(struct table *tbl, int maxlimit) +{ + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + struct table *t; + int i, col, row; + int indent, maxwidth; + + for (i = 0; i < tbl->ntable; i++) { + t = tbl->tables[i].ptr; + col = tbl->tables[i].col; + row = tbl->tables[i].row; + indent = tbl->tables[i].indent; + + init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf, + get_spec_cell_width(tbl, row, col), indent); + check_row(tbl, row); + if (h_env.limit > maxlimit) + h_env.limit = maxlimit; + if (t->total_width == 0) + maxwidth = h_env.limit - indent; + else if (t->total_width > 0) + maxwidth = t->total_width; + else + maxwidth = t->total_width = + -t->total_width * h_env.limit / 100; + renderTable(t, maxwidth, &h_env); + } +} + +static void +make_caption(struct table *t, struct html_feed_environ *h_env) +{ + struct html_feed_environ henv; + struct readbuffer obuf; + struct environment envs[MAX_ENV_LEVEL]; + TextLineList *tl; + Str tmp; + + if (t->caption->length <= 0) + return; + + if (t->total_width <= 0) + t->total_width = h_env->limit; + + init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextLineList(), + t->total_width, h_env->envs[h_env->envc].indent); + HTMLlineproc1("<center>", &henv); + HTMLlineproc0(t->caption->ptr, &henv, FALSE); + HTMLlineproc1("</center>", &henv); + + tl = henv.buf; + + if (tl->nitem > 0) { + TextLineListItem *ti; + tmp = Strnew_charp("<pre for_table>"); + for (ti = tl->first; ti != NULL; ti = ti->next) { + Strcat(tmp, ti->ptr->line); + Strcat_char(tmp, '\n'); + } + Strcat_charp(tmp, "</pre>"); + HTMLlineproc1(tmp->ptr, h_env); + } +} + +void +renderTable(struct table *t, + int max_width, + struct html_feed_environ *h_env) +{ + int i, j, w, r, h; + Str renderbuf = Strnew(); + short new_tabwidth[MAXCOL]; +#ifdef MATRIX + int itr; + VEC *newwidth; + MAT *mat, *minv; + PERM *pivot; +#endif /* MATRIX */ + int rulewidth; + Str vrulea, vruleb, vrulec; +#ifdef ID_EXT + Str idtag; +#endif /* ID_EXT */ + + t->total_height = 0; + if (t->maxcol < 0) { + make_caption(t, h_env); + return; + } + + if (t->sloppy_width > max_width) + max_width = t->sloppy_width; + + rulewidth = table_rule_width(t); + + max_width -= table_border_width(t); + + if (rulewidth > 1) + max_width = floor_at_intervals(max_width, rulewidth); + + if (max_width < rulewidth) + max_width = rulewidth; + + check_maximum_width(t); + +#ifdef MATRIX + if (t->maxcol == 0) { + if (t->tabwidth[0] > max_width) + t->tabwidth[0] = max_width; + if (t->total_width > 0) + t->tabwidth[0] = max_width; + else if (t->fixed_width[0] > 0) + t->tabwidth[0] = t->fixed_width[0]; + if (t->tabwidth[0] < t->minimum_width[0]) + t->tabwidth[0] = t->minimum_width[0]; + } + else { + set_table_matrix(t, max_width); + + itr = 0; + mat = m_get(t->maxcol + 1, t->maxcol + 1); + pivot = px_get(t->maxcol + 1); + newwidth = v_get(t->maxcol + 1); + minv = m_get(t->maxcol + 1, t->maxcol + 1); + do { + m_copy(t->matrix, mat); + LUfactor(mat, pivot); + LUsolve(mat, pivot, t->vector, newwidth); + LUinverse(mat, pivot, minv); +#ifdef TABLE_DEBUG + set_integered_width(t, newwidth->ve, new_tabwidth); + fprintf(stderr, "itr=%d\n", itr); + fprintf(stderr, "max_width=%d\n", max_width); + fprintf(stderr, "minimum : "); + for (i = 0; i <= t->maxcol; i++) + fprintf(stderr, "%2d ", t->minimum_width[i]); + fprintf(stderr, "\nfixed : "); + for (i = 0; i <= t->maxcol; i++) + fprintf(stderr, "%2d ", t->fixed_width[i]); + fprintf(stderr, "\ndecided : "); + for (i = 0; i <= t->maxcol; i++) + fprintf(stderr, "%2d ", new_tabwidth[i]); + fprintf(stderr, "\n"); +#endif /* TABLE_DEBUG */ + itr++; + + } while (check_table_width(t, newwidth->ve, minv, itr)); + set_integered_width(t, newwidth->ve, new_tabwidth); + check_minimum_width(t, new_tabwidth); + v_free(newwidth); + px_free(pivot); + m_free(mat); + m_free(minv); + m_free(t->matrix); + v_free(t->vector); + for (i = 0; i <= t->maxcol; i++) { + t->tabwidth[i] = new_tabwidth[i]; + } + } +#else /* not MATRIX */ + set_table_width(t, new_tabwidth, max_width); + for (i = 0; i <= t->maxcol; i++) { + t->tabwidth[i] = new_tabwidth[i]; + } +#endif /* not MATRIX */ + + check_minimum_width(t, t->tabwidth); + for (i = 0; i <= t->maxcol; i++) + t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth); + + renderCoTable(t, h_env->limit); + + for (i = 0; i <= t->maxcol; i++) { + for (j = 0; j <= t->maxrow; j++) { + check_row(t, j); + if (t->tabattr[j][i] & HTT_Y) + continue; + do_refill(t, j, i, h_env->limit); + } + } + + check_minimum_width(t, t->tabwidth); + t->total_width = 0; + for (i = 0; i <= t->maxcol; i++) { + t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth); + t->total_width += t->tabwidth[i]; + } + + t->total_width += table_border_width(t); + + check_table_height(t); + + for (i = 0; i <= t->maxcol; i++) { + for (j = 0; j <= t->maxrow; j++) { + TextLineList *l; + int k; + if ((t->tabattr[j][i] & HTT_Y) || + (t->tabattr[j][i] & HTT_TOP) || + (t->tabdata[j][i] == NULL)) + continue; + h = t->tabheight[j]; + for (k = j + 1; k <= t->maxrow; k++) { + if (!(t->tabattr[k][i] & HTT_Y)) + break; + h += t->tabheight[k]; + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + h += 1; + break; + } + } + h -= t->tabdata[j][i]->nitem; + if (t->tabattr[j][i] & HTT_MIDDLE) + h /= 2; + if (h <= 0) + continue; + l = newTextLineList(); + for (k = 0; k < h; k++) + pushTextLine(l, newTextLine(NULL, 0)); + t->tabdata[j][i] = appendGeneralList((GeneralList *)l, + t->tabdata[j][i]); + } + } + + /* table output */ + make_caption(t, h_env); + + HTMLlineproc1("<pre for_table>", h_env); +#ifdef ID_EXT + if (t->id != NULL) { + idtag = Sprintf("<_id id=\"%s\">", htmlquote_str((t->id)->ptr)); + HTMLlineproc1(idtag->ptr, h_env); + } +#endif /* ID_EXT */ + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + renderbuf = Strnew(); + print_sep(t, -1, T_TOP, t->maxcol, renderbuf); + push_render_image(renderbuf, t->total_width, h_env); + t->total_height += 1; + break; + } + vruleb = Strnew(); + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + vrulea = Strnew(); + vrulec = Strnew(); + Strcat_charp(vrulea, TK_VERTICALBAR(t->border_mode)); + for (i = 0; i < t->cellpadding; i++) { + Strcat_char(vrulea, ' '); + Strcat_char(vruleb, ' '); + Strcat_char(vrulec, ' '); + } + Strcat_charp(vrulec, TK_VERTICALBAR(t->border_mode)); + case BORDER_NOWIN: +#if defined(__EMX__)&&!defined(JP_CHARSET) + Strcat_charp(vruleb, CodePage==850?"\263":TN_VERTICALBAR); +#else + Strcat_charp(vruleb, TN_VERTICALBAR); +#endif + for (i = 0; i < t->cellpadding; i++) + Strcat_char(vruleb, ' '); + break; + case BORDER_NONE: + for (i = 0; i < t->cellspacing; i++) + Strcat_char(vruleb, ' '); + } + + for (r = 0; r <= t->maxrow; r++) { + for (h = 0; h < t->tabheight[r]; h++) { + renderbuf = Strnew(); + if (t->border_mode == BORDER_THIN || t->border_mode == BORDER_THICK) + Strcat(renderbuf, vrulea); +#ifdef ID_EXT + if (t->tridvalue[r] != NULL && h == 0) { + idtag = Sprintf("<_id id=\"%s\">", + htmlquote_str((t->tridvalue[r])->ptr)); + Strcat(renderbuf, idtag); + } +#endif /* ID_EXT */ + for (i = 0; i <= t->maxcol; i++) { + check_row(t, r); +#ifdef ID_EXT + if (t->tabidvalue[r][i] != NULL && h == 0) { + idtag = Sprintf("<_id id=\"%s\">", + htmlquote_str((t->tabidvalue[r][i])->ptr)); + Strcat(renderbuf, idtag); + } +#endif /* ID_EXT */ + if (!(t->tabattr[r][i] & HTT_X)) { + w = t->tabwidth[i]; + for (j = i + 1; + j <= t->maxcol && (t->tabattr[r][j] & HTT_X); + j++) + w += t->tabwidth[j] + t->cellspacing; + if (t->tabattr[r][i] & HTT_Y) { + for (j = r - 1; + j >= 0 && t->tabattr[j] && (t->tabattr[j][i] & HTT_Y); + j--); + print_item(t, j, i, w, renderbuf); + } + else + print_item(t, r, i, w, renderbuf); + } + if (i < t->maxcol && !(t->tabattr[r][i + 1] & HTT_X)) + Strcat(renderbuf, vruleb); + } + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + Strcat(renderbuf, vrulec); + t->total_height += 1; + break; + } + push_render_image(renderbuf, t->total_width, h_env); + } + if (r < t->maxrow && t->border_mode != BORDER_NONE) { + renderbuf = Strnew(); + print_sep(t, r, T_MIDDLE, t->maxcol, renderbuf); + push_render_image(renderbuf, t->total_width, h_env); + } + t->total_height += t->tabheight[r]; + } + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + renderbuf = Strnew(); + print_sep(t, t->maxrow, T_BOTTOM, t->maxcol, renderbuf); + push_render_image(renderbuf, t->total_width, h_env); + t->total_height += 1; + break; + } + if (t->total_height == 0) { + renderbuf = Strnew(" "); + t->total_height++; + t->total_width = 1; + push_render_image(renderbuf, t->total_width, h_env); + } + HTMLlineproc1("</pre>", h_env); +} + +#ifdef TABLE_NO_COMPACT +#define THR_PADDING 2 +#else +#define THR_PADDING 4 +#endif + +struct table * +begin_table(int border, int spacing, int padding, int vspace) +{ + struct table *t; + int mincell = minimum_cellspacing(border); + int rcellspacing; + int mincell_pixels = round(mincell * pixel_per_char); + int ppc = round(pixel_per_char); + + t = newTable(); + t->row = t->col = -1; + t->maxcol = -1; + t->maxrow = -1; + t->border_mode = border; + t->flag = 0; + if (border == BORDER_NOWIN) + t->flag |= TBL_EXPAND_OK; + + rcellspacing = spacing + 2 * padding; + switch (border) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + t->cellpadding = padding - (mincell_pixels - 4) / 2; + break; + case BORDER_NONE: + t->cellpadding = rcellspacing - mincell_pixels; + } + if (t->cellpadding >= ppc) + t->cellpadding /= ppc; + else if (t->cellpadding > 0) + t->cellpadding = 1; + else + t->cellpadding = 0; + + switch (border) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + t->cellspacing = 2 * t->cellpadding + mincell; + break; + case BORDER_NONE: + t->cellspacing = t->cellpadding + mincell; + } + + if (border == BORDER_NONE) { + if (rcellspacing / 2 + vspace <= 1) + t->vspace = 0; + else + t->vspace = 1; + } + else { + if (vspace < ppc) + t->vspace = 0; + else + t->vspace = 1; + } + + if (border == BORDER_NONE) { + if (rcellspacing <= THR_PADDING) + t->vcellpadding = 0; + else + t->vcellpadding = 1; + } + else { + if (padding < 2 * ppc - 2) + t->vcellpadding = 0; + else + t->vcellpadding = 1; + } + + return t; +} + +void +end_table(struct table *tbl) +{ + struct table_cell *cell = &tbl->cell; + int i, rulewidth = table_rule_width(tbl); + if (rulewidth > 1) { + if (tbl->total_width > 0) + tbl->total_width = + ceil_at_intervals(tbl->total_width, rulewidth); + for (i = 0; i <= tbl->maxcol; i++) { + tbl->minimum_width[i] = + ceil_at_intervals(tbl->minimum_width[i], rulewidth); + tbl->tabwidth[i] = + ceil_at_intervals(tbl->tabwidth[i], rulewidth); + if (tbl->fixed_width[i] > 0) + tbl->fixed_width[i] = + ceil_at_intervals(tbl->fixed_width[i], rulewidth); + } + for (i = 0; i <= cell->maxcell; i++) { + cell->minimum_width[i] = + ceil_at_intervals(cell->minimum_width[i], rulewidth); + cell->width[i] = + ceil_at_intervals(cell->width[i], rulewidth); + if (cell->fixed_width[i] > 0) + cell->fixed_width[i] = + ceil_at_intervals(cell->fixed_width[i], rulewidth); + } + } + tbl->sloppy_width = fixed_table_width(tbl); + if (tbl->total_width > tbl->sloppy_width) + tbl->sloppy_width = tbl->total_width; +} + +static void +check_minimum0(struct table *t, int min) +{ + int i, w, ww; + struct table_cell *cell; + + if (t->col < 0) + return; + if (t->tabwidth[t->col] < 0) + return; + check_row(t, t->row); + w = table_colspan(t, t->row, t->col); + min += t->indent; + if (w == 1) + ww = min; + else { + cell = &t->cell; + ww = 0; + if (cell->icell >= 0 && cell->minimum_width[cell->icell] < min) + cell->minimum_width[cell->icell] = min; + } + for (i = t->col; + i <= t->maxcol && (i == t->col || (t->tabattr[t->row][i] & HTT_X)); + i++) { + if (t->minimum_width[i] < ww) + t->minimum_width[i] = ww; + } +} + +static int +setwidth0(struct table *t, struct table_mode *mode) +{ + int w; + int width = t->tabcontentssize; + struct table_cell *cell = &t->cell; + + if (t->col < 0) + return -1; + if (t->tabwidth[t->col] < 0) + return -1; + check_row(t, t->row); + if (t->linfo.prev_spaces > 0) + width -= t->linfo.prev_spaces; + w = table_colspan(t, t->row, t->col); + if (w == 1) { + if (t->tabwidth[t->col] < width) + t->tabwidth[t->col] = width; + } + else if (cell->icell >= 0) { + if (cell->width[cell->icell] < width) + cell->width[cell->icell] = width; + } + return width; +} + +static void +setwidth(struct table *t, struct table_mode *mode) +{ + int width = setwidth0(t, mode); + if (width < 0) + return; +#ifdef NOWRAP + if (t->tabattr[t->row][t->col] & HTT_NOWRAP) + check_minimum0(t, width); +#endif /* NOWRAP */ + if (mode->pre_mode & (TBLM_NOBR | TBLM_PRE | TBLM_PRE_INT) && + mode->nobr_offset >= 0) + check_minimum0(t, width - mode->nobr_offset); +} + +static void +addcontentssize(struct table *t, int width) +{ + + if (t->col < 0) + return; + if (t->tabwidth[t->col] < 0) + return; + check_row(t, t->row); + t->tabcontentssize += width; +} + +static void table_close_anchor0(struct table *tbl, struct table_mode *mode); + +static void +clearcontentssize(struct table *t, struct table_mode *mode) +{ + table_close_anchor0(t, mode); + mode->nobr_offset = 0; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; + t->linfo.prev_ctype = PC_ASCII; + t->linfo.length = 0; + t->tabcontentssize = 0; +} + +static void +begin_cell(struct table *t, struct table_mode *mode) +{ + clearcontentssize(t, mode); + mode->indent_level = 0; + mode->nobr_level = 0; + mode->pre_mode = 0; + t->indent = 0; + t->flag |= TBL_IN_COL; + + if (t->suspended_data) { + check_row(t, t->row); + if (t->tabdata[t->row][t->col] == NULL) + t->tabdata[t->row][t->col] = newGeneralList(); + appendGeneralList(t->tabdata[t->row][t->col], + (GeneralList *)t->suspended_data); + t->suspended_data = NULL; + } +} + +void +check_rowcol(struct table *tbl, struct table_mode *mode) +{ + int row = tbl->row, col = tbl->col; + + if (!(tbl->flag & TBL_IN_ROW)) { + tbl->flag |= TBL_IN_ROW; + tbl->row++; + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + tbl->col = -1; + } + if (tbl->row == -1) + tbl->row = 0; + if (tbl->col == -1) + tbl->col = 0; + + for (;; tbl->row++) { + check_row(tbl, tbl->row); + for (; tbl->col < MAXCOL && + tbl->tabattr[tbl->row][tbl->col] & (HTT_X | HTT_Y); + tbl->col++); + if (tbl->col < MAXCOL) + break; + tbl->col = 0; + } + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + if (tbl->col > tbl->maxcol) + tbl->maxcol = tbl->col; + + if (tbl->row != row || tbl->col != col) + begin_cell(tbl, mode); + tbl->flag |= TBL_IN_COL; +} + +int +skip_space(struct table *t, char *line, struct table_linfo *linfo, + int checkminimum) +{ + int skip = 0, s = linfo->prev_spaces; + Lineprop ctype, prev_ctype = linfo->prev_ctype; + int prevchar = linfo->prevchar; + int w = linfo->length; + int min = 1; + + if (*line == '<' && line[strlen(line) - 1] == '>') { + if (checkminimum) + check_minimum0(t, visible_length(line)); + return 0; + } + + while (*line) { + char *save = line; + int ec = '\0', len, wlen, c; + ctype = get_mctype(line); + wlen = len = get_mclen(ctype); + c = mctowc(line, ctype); + if (min < w) + min = w; + if (ctype == PC_ASCII && IS_SPACE(c)) { + w = 0; + s++; + } + else { + if (c == '&') { + ec = getescapechar(&line); + if (ec) { + c = ec; + ctype = IS_CNTRL(ec) ? PC_CTRL : PC_ASCII; + len = strlen(conv_latin1(ec)); + wlen = line - save; + } + } + if (prevchar && is_boundary(prevchar, c)) { + w = len; + } + else { + w += len; + } + if (s > 0) { +#ifdef JP_CHARSET + if (ctype == PC_KANJI && prev_ctype == PC_KANJI) + skip += s; + else +#endif /* JP_CHARSET */ + skip += s - 1; + } + s = 0; + prev_ctype = ctype; + } + prevchar = c; + line = save + wlen; + } + if (s > 1) { + skip += s - 1; + linfo->prev_spaces = 1; + } + else { + linfo->prev_spaces = s; + } + linfo->prev_ctype = prev_ctype; + linfo->prevchar = prevchar; + + if (checkminimum) { + if (min < w) + min = w; + linfo->length = w; + check_minimum0(t, min); + } + return skip; +} + +static void +feed_table_inline_tag(struct table *tbl, + char *line, + struct table_mode *mode, + int width) +{ + check_rowcol(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + if (width >= 0) { + check_minimum0(tbl, width); + addcontentssize(tbl, width); + setwidth(tbl, mode); + } +} + +static void +feed_table_block_tag(struct table *tbl, + char *line, + struct table_mode *mode, + int indent, + int cmd) +{ + int offset; + if (mode->indent_level <= 0 && indent == -1) + return; + setwidth(tbl, mode); + feed_table_inline_tag(tbl, line, mode, -1); + clearcontentssize(tbl, mode); + if (indent == 1) { + mode->indent_level++; + if (mode->indent_level <= MAX_INDENT_LEVEL) + tbl->indent += INDENT_INCR; + } + else if (indent == -1) { + mode->indent_level--; + if (mode->indent_level < MAX_INDENT_LEVEL) + tbl->indent -= INDENT_INCR; + } + offset = tbl->indent; + if (cmd == HTML_DT) { + if (mode->indent_level > 0 && mode->indent_level <= MAX_INDENT_LEVEL) + offset -= INDENT_INCR; + } + if (tbl->indent > 0) { + check_minimum0(tbl, 0); + addcontentssize(tbl, offset); + } +} + +static void +table_close_select(struct table *tbl, struct table_mode *mode, int width) +{ + Str tmp = process_n_select(); + mode->pre_mode &= ~TBLM_INSELECT; + feed_table1(tbl, tmp, mode, width); +} + +static void +table_close_textarea(struct table *tbl, struct table_mode *mode, int width) +{ + Str tmp = process_n_textarea(); + mode->pre_mode &= ~TBLM_INTXTA; + feed_table1(tbl, tmp, mode, width); +} + +static void +table_close_anchor0(struct table *tbl, struct table_mode *mode) +{ + if (!(mode->pre_mode & TBLM_ANCHOR)) + return; + mode->pre_mode &= ~TBLM_ANCHOR; + if (tbl->tabcontentssize == mode->anchor_offset) { + check_minimum0(tbl, 1); + addcontentssize(tbl, 1); + setwidth(tbl, mode); + } + else if (tbl->linfo.prev_spaces > 0 && + tbl->tabcontentssize - 1 == mode->anchor_offset) { + if(tbl->linfo.prev_spaces > 0) + tbl->linfo.prev_spaces = -1; + } +} + +#define TAG_ACTION_NONE 0 +#define TAG_ACTION_FEED 1 +#define TAG_ACTION_TABLE 2 +#define TAG_ACTION_N_TABLE 3 + +#define CASE_TABLE_TAG \ + case HTML_TABLE:\ + case HTML_N_TABLE:\ + case HTML_TR:\ + case HTML_N_TR:\ + case HTML_TD:\ + case HTML_N_TD:\ + case HTML_TH:\ + case HTML_N_TH:\ + case HTML_THEAD:\ + case HTML_N_THEAD:\ + case HTML_TBODY:\ + case HTML_N_TBODY:\ + case HTML_TFOOT:\ + case HTML_N_TFOOT:\ + case HTML_COLGROUP:\ + case HTML_N_COLGROUP:\ + case HTML_COL + +static int +feed_table_tag(struct table *tbl, char *line, struct table_mode *mode, int width, struct parsed_tag *tag) +{ + int cmd; + char *p; + struct table_cell *cell = &tbl->cell; + int colspan, rowspan; + int col, prev_col; + int i, j, k, v, v0, w, id; + Str tok, tmp, anchor; + table_attr align, valign; + + cmd = tag->tagid; + + switch (cmd) { + CASE_TABLE_TAG: + if (mode->caption) + mode->caption = 0; + if (mode->pre_mode & (TBLM_IGNORE|TBLM_XMP|TBLM_LST)) + mode->pre_mode &= ~(TBLM_IGNORE|TBLM_XMP|TBLM_LST); + if (mode->pre_mode & TBLM_INTXTA) + table_close_textarea(tbl, mode, width); + if (mode->pre_mode & TBLM_INSELECT) + table_close_select(tbl, mode, width); + } + + if (mode->caption) { + switch (cmd) { + case HTML_N_CAPTION: + mode->caption = 0; + return TAG_ACTION_NONE; + default: + return TAG_ACTION_FEED; + } + } + + if (mode->pre_mode & TBLM_IGNORE) { + switch (cmd) { + case HTML_N_STYLE: + mode->pre_mode &= ~TBLM_STYLE; + case HTML_N_SCRIPT: + mode->pre_mode &= ~TBLM_SCRIPT; + default: + return TAG_ACTION_NONE; + } + } + + /* failsafe: a tag other than <option></option>and </select> in * + * <select> environment is regarded as the end of <select>. */ + if (mode->pre_mode & TBLM_INSELECT && cmd == HTML_N_FORM) { + table_close_select(tbl, mode, width); + } + + if ( + (mode->pre_mode & TBLM_INSELECT && cmd != HTML_N_SELECT) || + (mode->pre_mode & TBLM_INTXTA && cmd != HTML_N_TEXTAREA) || + (mode->pre_mode & TBLM_XMP && cmd != HTML_N_XMP) || + (mode->pre_mode & TBLM_LST && cmd != HTML_N_LISTING)) + return TAG_ACTION_FEED; + + if (mode->pre_mode & TBLM_PRE) { + switch (cmd) { + case HTML_NOBR: + case HTML_N_NOBR: + case HTML_PRE_INT: + case HTML_N_PRE_INT: + return TAG_ACTION_NONE; + } + } + + switch (cmd) { + case HTML_TABLE: + check_rowcol(tbl, mode); + return TAG_ACTION_TABLE; + case HTML_N_TABLE: + if (tbl->suspended_data) + check_rowcol(tbl, mode); + return TAG_ACTION_N_TABLE; + case HTML_TR: + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + tbl->col = -1; + tbl->row++; + tbl->flag |= TBL_IN_ROW; + tbl->flag &= ~TBL_IN_COL; + align = 0; + valign = 0; + if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) { + switch (i) { + case ALIGN_LEFT: + align = (HTT_LEFT | HTT_TRSET); + break; + case ALIGN_RIGHT: + align = (HTT_RIGHT | HTT_TRSET); + break; + case ALIGN_CENTER: + align = (HTT_CENTER | HTT_TRSET); + break; + } + } + if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) { + switch (i) { + case VALIGN_TOP: + valign = (HTT_TOP | HTT_VTRSET); + break; + case VALIGN_MIDDLE: + valign = (HTT_MIDDLE | HTT_VTRSET); + break; + case VALIGN_BOTTOM: + valign = (HTT_BOTTOM | HTT_VTRSET); + break; + } + } +#ifdef ID_EXT + if (parsedtag_get_value(tag, ATTR_ID, &p)) + tbl->tridvalue[tbl->row] = Strnew_charp(p); +#endif /* ID_EXT */ + tbl->trattr = align | valign; + break; + case HTML_TH: + case HTML_TD: + prev_col = tbl->col; + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + if (tbl->row == -1) { + /* for broken HTML... */ + tbl->row = -1; + tbl->col = -1; + tbl->maxrow = tbl->row; + } + if (tbl->col == -1) { + if (!(tbl->flag & TBL_IN_ROW)) { + tbl->row++; + tbl->flag |= TBL_IN_ROW; + } + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + } + tbl->col++; + check_row(tbl, tbl->row); + while (tbl->tabattr[tbl->row][tbl->col]) { + tbl->col++; + } + if (tbl->col > MAXCOL - 1) { + tbl->col = prev_col; + return TAG_ACTION_NONE; + } + if (tbl->col > tbl->maxcol) { + tbl->maxcol = tbl->col; + } + colspan = rowspan = 1; + if (tbl->trattr & HTT_TRSET) + align = (tbl->trattr & HTT_ALIGN); + else if (cmd == HTML_TH) + align = HTT_CENTER; + else + align = HTT_LEFT; + if (tbl->trattr & HTT_VTRSET) + valign = (tbl->trattr & HTT_VALIGN); + else + valign = HTT_MIDDLE; + if (parsedtag_get_value(tag, ATTR_ROWSPAN, &rowspan)) { + if ((tbl->row + rowspan) >= tbl->max_rowsize) + check_row(tbl, tbl->row + rowspan); + } + if (parsedtag_get_value(tag, ATTR_COLSPAN, &colspan)) { + if ((tbl->col + colspan) >= MAXCOL) { + /* Can't expand column */ + colspan = MAXCOL - tbl->col; + } + } + if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) { + switch (i) { + case ALIGN_LEFT: + align = HTT_LEFT; + break; + case ALIGN_RIGHT: + align = HTT_RIGHT; + break; + case ALIGN_CENTER: + align = HTT_CENTER; + break; + } + } + if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) { + switch (i) { + case VALIGN_TOP: + valign = HTT_TOP; + break; + case VALIGN_MIDDLE: + valign = HTT_MIDDLE; + break; + case VALIGN_BOTTOM: + valign = HTT_BOTTOM; + break; + } + } +#ifdef NOWRAP + if (parsedtag_exists(tag, ATTR_NOWRAP)) + tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP; +#endif /* NOWRAP */ + v = 0; + if (parsedtag_get_value(tag, ATTR_WIDTH, &v)) { +#ifdef TABLE_EXPAND + if (v > 0) { + if (tbl->real_width > 0) + v = - (v * 100) / (tbl->real_width * pixel_per_char); + else + v = (int)(v / pixel_per_char); + } +#else + v = RELATIVE_WIDTH(v); +#endif /* not TABLE_EXPAND */ + } +#ifdef ID_EXT + if (parsedtag_get_value(tag, ATTR_ID, &p)) + tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(p); +#endif /* ID_EXT */ +#ifdef NOWRAP + if (v != 0) { + /* NOWRAP and WIDTH= conflicts each other */ + tbl->tabattr[tbl->row][tbl->col] &= ~HTT_NOWRAP; + } +#endif /* NOWRAP */ + tbl->tabattr[tbl->row][tbl->col] &= ~(HTT_ALIGN | HTT_VALIGN); + tbl->tabattr[tbl->row][tbl->col] |= (align | valign); + if (colspan > 1) { + col = tbl->col; + + cell->icell = cell->maxcell + 1; + k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL, + cell->index, cell->icell); + if (k <= cell->maxcell) { + i = cell->index[k]; + if (cell->col[i] == col && + cell->colspan[i] == colspan) + cell->icell = i; + } + if (cell->icell > cell->maxcell && cell->icell < MAXCELL) { + cell->maxcell++; + cell->col[cell->maxcell] = col; + cell->colspan[cell->maxcell] = colspan; + cell->width[cell->maxcell] = 0; + cell->minimum_width[cell->maxcell] = 0; + cell->fixed_width[cell->maxcell] = 0; + if (cell->maxcell > k) + bcopy(cell->index + k, cell->index + k + 1, cell->maxcell - k); + cell->index[k] = cell->maxcell; + } + if (cell->icell > cell->maxcell) + cell->icell = -1; + } + if (v != 0) { + if (colspan == 1) { + v0 = tbl->fixed_width[tbl->col]; + if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) { +#ifdef FEED_TABLE_DEBUG + fprintf(stderr, "width(%d) = %d\n", tbl->col, v); +#endif /* TABLE_DEBUG */ + tbl->fixed_width[tbl->col] = v; + } + } + else if (cell->icell >= 0) { + v0 = cell->fixed_width[cell->icell]; + if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) + cell->fixed_width[cell->icell] = v; + } + } + for (i = 0; i < rowspan; i++) { + check_row(tbl, tbl->row + i); + for (j = 0; j < colspan; j++) { + tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y); + tbl->tabattr[tbl->row + i][tbl->col + j] |= + ((i > 0) ? HTT_Y : 0) | ((j > 0) ? HTT_X : 0); + if (tbl->col + j > tbl->maxcol) { + tbl->maxcol = tbl->col + j; + } + } + if (tbl->row + i > tbl->maxrow) { + tbl->maxrow = tbl->row + i; + } + } + begin_cell(tbl, mode); + break; + case HTML_N_TR: + setwidth(tbl, mode); + tbl->col = -1; + tbl->flag &= ~(TBL_IN_ROW | TBL_IN_COL); + return TAG_ACTION_NONE; + case HTML_N_TH: + case HTML_N_TD: + setwidth(tbl, mode); + tbl->flag &= ~TBL_IN_COL; +#ifdef FEED_TABLE_DEBUG + { + TextListItem *it; + int i = tbl->col, j = tbl->row; + fprintf(stderr, "(a) row,col: %d, %d\n", j, i); + if (tbl->tabdata[j] && tbl->tabdata[j][i]) { + for (it = ((TextList *)tbl->tabdata[j][i])->first; + it; it = it->next) + fprintf(stderr, " [%s] \n", it->ptr); + } + } +#endif + return TAG_ACTION_NONE; + case HTML_P: + case HTML_BR: + case HTML_DT: + case HTML_DD: + case HTML_CENTER: + case HTML_N_CENTER: + case HTML_DIV: + case HTML_N_DIV: + case HTML_H: + case HTML_N_H: + case HTML_LI: + case HTML_PRE: + case HTML_N_PRE: + case HTML_LISTING: + case HTML_N_LISTING: + case HTML_XMP: + case HTML_N_XMP: + feed_table_block_tag(tbl, line, mode, 0, cmd); + switch (cmd) { + case HTML_PRE: + mode->pre_mode |= TBLM_PRE; + break; + case HTML_N_PRE: + mode->pre_mode &= ~TBLM_PRE; + break; + case HTML_LISTING: + mode->pre_mode |= TBLM_LST; + break; + case HTML_N_LISTING: + mode->pre_mode &= ~TBLM_LST; + break; + case HTML_XMP: + mode->pre_mode |= TBLM_XMP; + break; + case HTML_N_XMP: + mode->pre_mode &= ~TBLM_XMP; + break; + } + break; + case HTML_DL: + case HTML_BLQ: + case HTML_OL: + case HTML_UL: + feed_table_block_tag(tbl, line, mode, 1, cmd); + break; + case HTML_N_DL: + case HTML_N_BLQ: + case HTML_N_OL: + case HTML_N_UL: + feed_table_block_tag(tbl, line, mode, -1, cmd); + break; + case HTML_PRE_INT: + case HTML_NOBR: + case HTML_WBR: + feed_table_inline_tag(tbl, line, mode, -1); + switch (cmd) { + case HTML_NOBR: + mode->nobr_level++; + if (mode->pre_mode & TBLM_NOBR) + return TAG_ACTION_NONE; + mode->pre_mode |= TBLM_NOBR; + break; + case HTML_PRE_INT: + if (mode->pre_mode & TBLM_PRE_INT) + return TAG_ACTION_NONE; + mode->pre_mode |= TBLM_PRE_INT; + tbl->linfo.prev_spaces = 0; + break; + } + mode->nobr_offset = -1; + if (tbl->linfo.length > 0) { + check_minimum0(tbl, tbl->linfo.length); + tbl->linfo.length = 0; + } + break; + case HTML_N_NOBR: + feed_table_inline_tag(tbl, line, mode, -1); + if (mode->nobr_level > 0) + mode->nobr_level--; + if (mode->nobr_level == 0) + mode->pre_mode &= ~TBLM_NOBR; + break; + case HTML_N_PRE_INT: + feed_table_inline_tag(tbl, line, mode, -1); + mode->pre_mode &= ~TBLM_PRE_INT; + break; + case HTML_IMG: + tok = process_img(tag); + feed_table1(tbl, tok, mode, width); + break; +#ifdef NEW_FORM + case HTML_FORM: + process_form(tag); + break; + case HTML_N_FORM: + process_n_form(); + break; +#else /* not NEW_FORM */ + case HTML_FORM: + case HTML_N_FORM: + feed_table_block_tag(tbl, "<br>", mode, 0, HTML_BR); + if (line[1] == '/') { + tok = Strnew_charp("</form_int "); + Strcat_charp(tok, line + 6); + } + else { + tok = Strnew_charp("<form_int "); + Strcat_charp(tok, line + 5); + } + pushdata(tbl, tbl->row, tbl->col, tok->ptr); + break; +#endif /* not NEW_FORM */ + case HTML_INPUT: + tmp = process_input(tag); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_SELECT: + process_select(tag); + mode->pre_mode |= TBLM_INSELECT; + break; + case HTML_N_SELECT: + table_close_select(tbl, mode, width); + break; + case HTML_OPTION: + /* nothing */ + break; + case HTML_TEXTAREA: + w = 0; + check_rowcol(tbl, mode); + if (tbl->col + 1 <= tbl->maxcol && + tbl->tabattr[tbl->row][tbl->col + 1] & HTT_X) { + if (cell->icell >= 0 && cell->fixed_width[cell->icell] > 0) + w = cell->fixed_width[cell->icell]; + } + else { + if (tbl->fixed_width[tbl->col] > 0) + w = tbl->fixed_width[tbl->col]; + } + tmp = process_textarea(tag, w); + feed_table1(tbl, tmp, mode, width); + mode->pre_mode |= TBLM_INTXTA; + break; + case HTML_N_TEXTAREA: + table_close_textarea(tbl, mode, width); + break; + case HTML_A: + table_close_anchor0(tbl, mode); + anchor = NULL; + i = 0; + parsedtag_get_value(tag, ATTR_HREF, &anchor); + parsedtag_get_value(tag, ATTR_HSEQ, &i); + if (anchor) { + check_rowcol(tbl, mode); + if (i == 0) { + Str tmp = process_anchor(tag, line); + pushdata(tbl, tbl->row, tbl->col, tmp->ptr); + } + else + pushdata(tbl, tbl->row, tbl->col, line); + if (i >= 0) { + mode->pre_mode |= TBLM_ANCHOR; + mode->anchor_offset = tbl->tabcontentssize; + } + } + else + suspend_or_pushdata(tbl, line); + break; + case HTML_DEL: + case HTML_N_DEL: + case HTML_INS: + case HTML_N_INS: + feed_table_inline_tag(tbl, line, mode, 5); + break; + case HTML_TABLE_ALT: + id = -1; + w = 0; + parsedtag_get_value(tag, ATTR_TID, &id); + if (id >= 0 && id < tbl->ntable) { + struct table *tbl1 = tbl->tables[id].ptr; + feed_table_block_tag(tbl, line, mode, 0, cmd); + addcontentssize(tbl, maximum_table_width(tbl1)); + check_minimum0(tbl, tbl1->sloppy_width); +#ifdef TABLE_EXPAND + w = tbl1->total_width; + v = 0; + colspan = table_colspan(tbl, tbl->row, tbl->col); + if (colspan > 1) { + if (cell->icell >= 0) + v = cell->fixed_width[cell->icell]; + } + else + v = tbl->fixed_width[tbl->col]; + if (v < 0 && tbl->real_width > 0 && tbl1->real_width > 0) + w = - (tbl1->real_width * 100) / tbl->real_width; + else + w = tbl1->real_width; + if (w > 0) + check_minimum0(tbl, w); + else if (w < 0 && v < w) { + if (colspan > 1) { + if (cell->icell >= 0) + cell->fixed_width[cell->icell] = w; + } + else + tbl->fixed_width[tbl->col] = w; + } +#endif + setwidth0(tbl, mode); + clearcontentssize(tbl, mode); + } + break; + case HTML_CAPTION: + mode->caption = 1; + break; + case HTML_THEAD: + case HTML_N_THEAD: + case HTML_TBODY: + case HTML_N_TBODY: + case HTML_TFOOT: + case HTML_N_TFOOT: + case HTML_COLGROUP: + case HTML_N_COLGROUP: + case HTML_COL: + break; + case HTML_SCRIPT: + mode->pre_mode |= TBLM_SCRIPT; + break; + case HTML_STYLE: + mode->pre_mode |= TBLM_STYLE; + break; + case HTML_N_A: + table_close_anchor0(tbl, mode); + case HTML_FONT: + case HTML_N_FONT: + case HTML_NOP: + suspend_or_pushdata(tbl, line); + break; + case HTML_FORM_INT: + case HTML_N_FORM_INT: + case HTML_INPUT_ALT: + case HTML_N_INPUT_ALT: + case HTML_IMG_ALT: + case HTML_EOL: + case HTML_RULE: + case HTML_N_RULE: + default: + /* unknown tag: put into table */ + return TAG_ACTION_FEED; + } + return TAG_ACTION_NONE; +} + + +int +feed_table(struct table *tbl, char *line, struct table_mode *mode, + int width, int internal) +{ + int i; + char *p; + Str tmp; + struct table_linfo *linfo = &tbl->linfo; + + if (*line == '<') { + int action; + struct parsed_tag *tag; + p = line; + tag = parse_tag(&p, internal); + if (tag) { + action = feed_table_tag(tbl, line, mode, width, tag); + if (action == TAG_ACTION_NONE) + return -1; + else if (action == TAG_ACTION_N_TABLE) + return 0; + else if (action == TAG_ACTION_TABLE) { + return 1; + } + else if (parsedtag_need_reconstruct(tag)) + line = parsedtag2str(tag)->ptr; + } + else { + if (!(mode->pre_mode & TBLM_PLAIN)) + return -1; + } + } + if (mode->caption) { + Strcat_charp(tbl->caption, line); + return -1; + } + if (mode->pre_mode & TBLM_IGNORE) + return -1; + if (mode->pre_mode & TBLM_INTXTA) { + feed_textarea(line); + return -1; + } + if (mode->pre_mode & TBLM_INSELECT) { + feed_select(line); + return -1; + } + if (!(mode->pre_mode & TBLM_PLAIN) && + !(*line == '<' && line[strlen(line) - 1] == '>') && + strchr(line, '&') != NULL) { + tmp = Strnew(); + for (p = line; *p;) { + char *q, *r; + if (*p == '&') { + if (!strncasecmp(p, "&", 5) || + !strncasecmp(p, ">", 4) || + !strncasecmp(p, "<", 4)) { + /* do not convert */ + Strcat_char(tmp, *p); + p++; + } + else { + int ec; + q = p; + ec = getescapechar(&p); + r = conv_latin1(ec); + if (r != NULL && strlen(r) == 1 && + ec == (unsigned char)*r) { + Strcat_char(tmp, *r); + } + else { + Strcat_char(tmp, *q); + p = q + 1; + } + } + } + else { + Strcat_char(tmp, *p); + p++; + } + } + line = tmp->ptr; + } + if (!(mode->pre_mode & TBLM_SPECIAL)) { + if (!(tbl->flag & TBL_IN_COL) || linfo->prev_spaces != 0) + while (IS_SPACE(*line)) + line++; + if (*line == '\0') + return -1; + check_rowcol(tbl, mode); + if (mode->pre_mode & TBLM_NOBR && mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + + /* count of number of spaces skipped in normal mode */ + i = skip_space(tbl, line, linfo, !(mode->pre_mode & TBLM_NOBR)); + addcontentssize(tbl, visible_length(line) - i); + setwidth(tbl, mode); + } + else { + /* <pre> mode or something like it */ + check_rowcol(tbl, mode); + if (mode->pre_mode & TBLM_PRE_INT && mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + if (mode->pre_mode & TBLM_PLAIN) + i = strlen(line); + else + i = maximum_visible_length(line); + addcontentssize(tbl, i); + setwidth(tbl, mode); + if (!(mode->pre_mode & TBLM_PRE_INT)) { + p = line + strlen(line) - 1; + if (*p == '\r' || *p == '\n') + clearcontentssize(tbl, mode); + } + } + pushdata(tbl, tbl->row, tbl->col, line); + return -1; +} + +void +feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width) +{ + Str tokbuf; + int status; + char *line; + if (!tok) + return; + tokbuf = Strnew(); + status = R_ST_NORMAL; + line = tok->ptr; + while (read_token(tokbuf, &line, &status, mode->pre_mode & TBLM_PREMODE, 0)) + feed_table(tbl, tokbuf->ptr, mode, width, TRUE); +} + +void +pushTable(struct table *tbl, struct table *tbl1) +{ + int col; + int row; + + col = tbl->col; + row = tbl->row; + + if (tbl->ntable >= tbl->tables_size) { + struct table_in *tmp; + tbl->tables_size += MAX_TABLE_N; + tmp = New_N(struct table_in, tbl->tables_size); + if (tbl->tables) +#ifdef __CYGWIN__ + bcopy((const char *) tbl->tables, (char *) tmp, (size_t) tbl->ntable * sizeof(struct table_in)); +#else /* not __CYGWIN__ */ + bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in)); +#endif /* not __CYGWIN__ */ + tbl->tables = tmp; + } + + tbl->tables[tbl->ntable].ptr = tbl1; + tbl->tables[tbl->ntable].col = col; + tbl->tables[tbl->ntable].row = row; + tbl->tables[tbl->ntable].indent = tbl->indent; + tbl->tables[tbl->ntable].buf = newTextLineList(); + check_row(tbl, row); + if (col + 1 <= tbl->maxcol && + tbl->tabattr[row][col + 1] & HTT_X) + tbl->tables[tbl->ntable].cell = tbl->cell.icell; + else + tbl->tables[tbl->ntable].cell = -1; + tbl->ntable++; +} + +#ifdef MATRIX +int +correct_table_matrix(struct table *t, int col, int cspan, int a, double b) +{ + int i, j; + int ecol = col + cspan; + double w = 1. / (b * b); + + for (i = col; i < ecol; i++) { + v_add_val(t->vector, i, w * a); + for (j = i; j < ecol; j++) { + m_add_val(t->matrix, i, j, w); + m_set_val(t->matrix, j, i, m_entry(t->matrix, i, j)); + } + } + return i; +} + +static void +correct_table_matrix2(struct table *t, int col, int cspan, double s, double b) +{ + int i, j; + int ecol = col + cspan; + int size = t->maxcol + 1; + double w = 1. / (b * b); + double ss; + + for (i = 0; i < size; i++) { + for (j = i; j < size; j++) { + if (i >= col && i < ecol && j >= col && j < ecol) + ss = (1. - s) * (1. - s); + else if ((i >= col && i < ecol) || (j >= col && j < ecol)) + ss = -(1. - s) * s; + else + ss = s * s; + m_add_val(t->matrix, i, j, w * ss); + } + } +} + +static void +correct_table_matrix3(struct table *t, int col, char *flags, double s, double b) +{ + int i, j; + double ss; + int size = t->maxcol + 1; + double w = 1. / (b * b); + int flg = (flags[col] == 0); + + for (i = 0; i < size; i++) { + if (!((flg && flags[i] == 0) || (!flg && flags[i] != 0))) + continue; + for (j = i; j < size; j++) { + if (!((flg && flags[j] == 0) || (!flg && flags[j] != 0))) + continue; + if (i == col && j == col) + ss = (1. - s) * (1. - s); + else if (i == col || j == col) + ss = -(1. - s) * s; + else + ss = s * s; + m_add_val(t->matrix, i, j, w * ss); + } + } +} + +static void +set_table_matrix0(struct table *t, int maxwidth) +{ + int size = t->maxcol + 1; + int i, j, k, bcol, ecol; + int swidth, width, a; + double w0, w1, w, s, b; +#ifdef __GNUC__ + double we[size]; + char expand[size]; +#else /* not __GNUC__ */ + double we[MAXCOL]; + char expand[MAXCOL]; +#endif /* not __GNUC__ */ + struct table_cell *cell = &t->cell; + + w0 = 0.; + for (i = 0; i < size; i++) { + we[i] = weight(t->tabwidth[i]); + w0 += we[i]; + } + if (w0 <= 0.) + w0 = 1.; + + if (cell->necell == 0) { + for (i = 0; i < size; i++) { + s = we[i] / w0; + b = sigma_td_nw((int) (s * maxwidth)); + correct_table_matrix2(t, i, 1, s, b); + } + return; + } + + bzero(expand, size); + + for (k = 0; k < cell->necell; k++) { + j = cell->eindex[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) { + swidth += t->tabwidth[i]; + expand[i]++; + } + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + w = weight(width); + w1 = 0.; + for (i = bcol; i < ecol; i++) + w1 += we[i]; + s = w / (w0 + w - w1); + a = (int) (s * maxwidth); + b = sigma_td_nw(a); + correct_table_matrix2(t, bcol, cell->colspan[j], s, b); + } + + w1 = 0.; + for (i = 0; i < size; i++) + if (expand[i] == 0) + w1 += we[i]; + for (i = 0; i < size; i++) { + if (expand[i] == 0) { + s = we[i] / max(w1, 1.); + b = sigma_td_nw((int) (s * maxwidth)); + } + else { + s = we[i] / max(w0 - w1, 1.); + b = sigma_td_nw(maxwidth); + } + correct_table_matrix3(t, i, expand, s, b); + } +} + +void +set_table_matrix(struct table *t, int width) +{ + int size = t->maxcol + 1; + int i, j; + double b, s; + int a; + struct table_cell *cell = &t->cell; + + if (size < 1) + return; + + t->matrix = m_get(size, size); + t->vector = v_get(size); + for (i = 0; i < size; i++) { + for (j = i; j < size; j++) + m_set_val(t->matrix, i, j, 0.); + v_set_val(t->vector, i, 0.); + } + + for (i = 0; i < size; i++) { + if (t->fixed_width[i] > 0) { + a = max(t->fixed_width[i], t->minimum_width[i]); + b = sigma_td(a); + correct_table_matrix(t, i, 1, a, b); + } + else if (t->fixed_width[i] < 0) { + s = -(double) t->fixed_width[i] / 100.; + b = sigma_td((int) (s * width)); + correct_table_matrix2(t, i, 1, s, b); + } + } + + for (j = 0; j <= cell->maxcell; j++) { + if (cell->fixed_width[j] > 0) { + a = max(cell->fixed_width[j], cell->minimum_width[j]); + b = sigma_td(a); + correct_table_matrix(t, cell->col[j], + cell->colspan[j], a, b); + } + else if (cell->fixed_width[j] < 0) { + s = -(double) cell->fixed_width[j] / 100.; + b = sigma_td((int) (s * width)); + correct_table_matrix2(t, cell->col[j], + cell->colspan[j], s, b); + } + } + + set_table_matrix0(t, width); + + if (t->total_width > 0) { + b = sigma_table(width); + } + else { + b = sigma_table_nw(width); + } + correct_table_matrix(t, 0, size, width, b); +} +#endif /* MATRIX */ + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ diff --git a/table.c.0 b/table.c.0 new file mode 100644 index 0000000..f3cacb1 --- /dev/null +++ b/table.c.0 @@ -0,0 +1,2878 @@ +/* -*- mode: c; c-basic-offset: 4; tab-width: 8; -*- */ +/* $Id: table.c.0,v 1.1 2001/11/08 05:15:52 a-ito Exp $ */ +/* + * HTML table + */ +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#ifdef __EMX__ +#include <strings.h> +#endif /* __EMX__ */ + +#include "fm.h" +#include "html.h" +#include "parsetag.h" +#include "Str.h" +#include "myctype.h" + +#ifdef KANJI_SYMBOLS +static char *rule[] = +{"��", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "}; +static char *ruleB[] = +{"00", "��", "��", "��", "��", "��", "��", "07", "��", "��", "��", "0B", "��", "0D", "0E", " "}; +#define TN_VERTICALBAR "��" +#define HORIZONTALBAR "��" +#define RULE_WIDTH 2 +#else /* not KANJI_SYMBOLS */ +static char *rule[] = +{ + "<_RULE TYPE=0>+</_RULE>", + "<_RULE TYPE=1>+</_RULE>", + "<_RULE TYPE=2>+</_RULE>", + "<_RULE TYPE=3>+</_RULE>", + "<_RULE TYPE=4>+</_RULE>", + "<_RULE TYPE=5>|</_RULE>", + "<_RULE TYPE=6>+</_RULE>", + "<_RULE TYPE=7>07</_RULE>", + "<_RULE TYPE=8>+</_RULE>", + "<_RULE TYPE=9>+</_RULE>", + "<_RULE TYPE=10>-</_RULE>", + "<_RULE TYPE=11>0B</_RULE>", + "<_RULE TYPE=12>+</_RULE>", + "<_RULE TYPE=13>0D</_RULE>", + "<_RULE TYPE=14>0E</_RULE>", + "<_RULE TYPE=15> </_RULE>"}; +static char **ruleB = rule; +#define TN_VERTICALBAR "<_RULE TYPE=5>|</_RULE>" +#define HORIZONTALBAR "<_RULE TYPE=10>-</_RULE>" +#define RULE_WIDTH 1 +#endif /* not KANJI_SYMBOLS */ + +#define RULE(mode) (((mode)==BORDER_THICK)?ruleB:rule) +#define TK_VERTICALBAR(mode) (RULE(mode)[5]) + +#define BORDERWIDTH 2 +#define BORDERHEIGHT 1 +#define NOBORDERWIDTH 1 +#define NOBORDERHEIGHT 0 + +#define HTT_X 1 +#define HTT_Y 2 +#define HTT_ALIGN 0x30 +#define HTT_LEFT 0x00 +#define HTT_CENTER 0x10 +#define HTT_RIGHT 0x20 +#define HTT_TRSET 0x40 +#ifdef NOWRAP +#define HTT_NOWRAP 4 +#endif /* NOWRAP */ +#define TAG_IS(s,tag,len) (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len]))) + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif /* not max */ +#ifndef min +#define min(a,b) ((a) > (b) ? (b) : (a)) +#endif /* not min */ +#ifndef abs +#define abs(a) ((a) >= 0. ? (a) : -(a)) +#endif /* not abs */ + +#ifdef MATRIX +#ifndef MESCHACH +#include "matrix.c" +#endif /* not MESCHACH */ +#endif /* MATRIX */ + +#ifdef MATRIX +int correct_table_matrix(struct table *, int, int, int, double); +void set_table_matrix(struct table *, int); +#endif /* MATRIX */ + +#ifdef MATRIX +static double +weight(int x) +{ + + if (x < COLS) + return (double) x; + else + return COLS * (log((double) x / COLS) + 1.); +} + +static double +weight2(int a) +{ + return (double) a / COLS * 4 + 1.; +} + +#define sigma_td(a) (0.5*weight2(a)) /* <td width=...> */ +#define sigma_td_nw(a) (32*weight2(a)) /* <td ...> */ +#define sigma_table(a) (0.25*weight2(a)) /* <table width=...> */ +#define sigma_table_nw(a) (2*weight2(a)) /* <table...> */ +#else /* not MATRIX */ +#define LOG_MIN 1.0 +static double +weight3(int x) +{ + if (x < 0.1) + return 0.1; + if (x < LOG_MIN) + return (double) x; + else + return LOG_MIN * (log((double) x / LOG_MIN) + 1.); +} +#endif /* not MATRIX */ + +static int +dsort_index(short e1, short *ent1, short e2, short *ent2, int base, + char *index, int nent) +{ + int n = nent; + int k = 0; + + int e = e1 * base + e2; + while (n > 0) { + int nn = n / 2; + int idx = index[k + nn]; + int ne = ent1[idx] * base + ent2[idx]; + if (ne == e) { + k += nn; + break; + } + else if (ne < e) { + n -= nn + 1; + k += nn + 1; + } + else { + n = nn; + } + } + return k; +} + +static int +fsort_index(double e, double *ent, char *index, int nent) +{ + int n = nent; + int k = 0; + + while (n > 0) { + int nn = n / 2; + int idx = index[k + nn]; + double ne = ent[idx]; + if (ne == e) { + k += nn; + break; + } + else if (ne > e) { + n -= nn + 1; + k += nn + 1; + } + else { + n = nn; + } + } + return k; +} + +static void +dv2sv(double *dv, short *iv, int size) +{ + int i, k, iw; + char *index; + double *edv; + double w = 0., x; + + index = NewAtom_N(char, size); + edv = NewAtom_N(double, size); + for (i = 0; i < size; i++) { + iv[i] = ceil(dv[i]); + edv[i] = (double) iv[i] - dv[i]; + } + + w = 0.; + for (k = 0; k < size; k++) { + x = edv[k]; + w += x; + i = fsort_index(x, edv, index, k); + if (k > i) + bcopy(index + i, index + i + 1, k - i); + index[i] = k; + } + iw = min((int) (w + 0.5), size); + if (iw == 0) + return; + x = edv[(int) index[iw - 1]]; + for (i = 0; i < size; i++) { + k = index[i]; + if (i >= iw && abs(edv[k] - x) > 1e-6) + break; + iv[k]--; + } +} + +static int +table_colspan(struct table *t, int row, int col) +{ + int i; + for (i = col + 1; i <= t->maxcol && (t->tabattr[row][i] & HTT_X); i++); + return i - col; +} + +static int +table_rowspan(struct table *t, int row, int col) +{ + int i; + if (!t->tabattr[row]) + return 0; + for (i = row + 1; i <= t->maxrow && t->tabattr[i] && + (t->tabattr[i][col] & HTT_Y); i++); + return i - row; +} + +static int +minimum_cellspacing(int border_mode) +{ + switch (border_mode) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + return RULE_WIDTH; + case BORDER_NONE: + return 1; + default: + /* not reached */ + return 0; + } +} + +static int +table_border_width(struct table *t) +{ + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + return t->maxcol * t->cellspacing + 2 * (RULE_WIDTH + t->cellpadding); + case BORDER_NOWIN: + case BORDER_NONE: + return t->maxcol * t->cellspacing; + default: + /* not reached */ + return 0; + } +} + +struct table * +newTable() +{ + struct table *t; + int i, j; + + t = New(struct table); + t->max_rowsize = MAXROW; + t->tabdata = New_N(TextList **, MAXROW); + t->tabattr = New_N(table_attr *, MAXROW); + t->tabheight = NewAtom_N(short, MAXROW); +#ifdef ID_EXT + t->tabidvalue = New_N(Str *, MAXROW); + t->tridvalue = New_N(Str, MAXROW); +#endif /* ID_EXT */ + + for (i = 0; i < MAXROW; i++) { + t->tabdata[i] = NULL; + t->tabattr[i] = 0; + t->tabheight[i] = 0; +#ifdef ID_EXT + t->tabidvalue[i] = NULL; + t->tridvalue[i] = NULL; +#endif /* ID_EXT */ + } + for (j = 0; j < MAXCOL; j++) { + t->tabwidth[j] = 0; + t->minimum_width[j] = 0; + t->fixed_width[j] = 0; + } + t->cell.maxcell = -1; + t->cell.icell = -1; + t->tabcontentssize = 0; + t->indent = 0; + t->ntable = 0; + t->tables_size = 0; + t->tables = NULL; +#ifdef MATRIX + t->matrix = NULL; + t->vector = NULL; +#endif /* MATRIX */ + t->linfo.prev_ctype = PC_ASCII; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; + t->trattr = 0; + + t->status = R_ST_NORMAL; + t->suspended_input = Strnew(); + t->caption = Strnew(); +#ifdef ID_EXT + t->id = NULL; +#endif + return t; +} + +static void +check_row(struct table *t, int row) +{ + int i, r; + TextList ***tabdata; + table_attr **tabattr; + short *tabheight; +#ifdef ID_EXT + Str **tabidvalue; + Str *tridvalue; +#endif /* ID_EXT */ + + if (row >= t->max_rowsize) { + r = max(t->max_rowsize * 2, row + 1); + tabdata = New_N(TextList **, r); + tabattr = New_N(table_attr *, r); + tabheight = New_N(short, r); +#ifdef ID_EXT + tabidvalue = New_N(Str *, r); + tridvalue = New_N(Str, r); +#endif /* ID_EXT */ + for (i = 0; i < t->max_rowsize; i++) { + tabdata[i] = t->tabdata[i]; + tabattr[i] = t->tabattr[i]; + tabheight[i] = t->tabheight[i]; +#ifdef ID_EXT + tabidvalue[i] = t->tabidvalue[i]; + tridvalue[i] = t->tridvalue[i]; +#endif /* ID_EXT */ + } + for (; i < r; i++) { + tabdata[i] = NULL; + tabattr[i] = NULL; + tabheight[i] = 0; +#ifdef ID_EXT + tabidvalue[i] = NULL; + tridvalue[i] = NULL; +#endif /* ID_EXT */ + } + t->tabdata = tabdata; + t->tabattr = tabattr; + t->tabheight = tabheight; +#ifdef ID_EXT + t->tabidvalue = tabidvalue; + t->tridvalue = tridvalue; +#endif /* ID_EXT */ + t->max_rowsize = r; + } + + if (t->tabdata[row] == NULL) { + t->tabdata[row] = New_N(TextList *, MAXCOL); + t->tabattr[row] = NewAtom_N(table_attr, MAXCOL); +#ifdef ID_EXT + t->tabidvalue[row] = New_N(Str, MAXCOL); +#endif /* ID_EXT */ + for (i = 0; i < MAXCOL; i++) { + t->tabdata[row][i] = NULL; + t->tabattr[row][i] = 0; +#ifdef ID_EXT + t->tabidvalue[row][i] = NULL; +#endif /* ID_EXT */ + } + } +} + +void +pushdata(struct table *t, int row, int col, char *data) +{ + check_row(t, row); + if (t->tabdata[row][col] == NULL) + t->tabdata[row][col] = newTextList(); + + pushText(t->tabdata[row][col], data); +} + +int visible_length_offset = 0; +int +visible_length(char *str) +{ + int len = 0; + int status = R_ST_NORMAL; + int prev_status = status; + Str tagbuf = Strnew(); + char *t, *r2; + struct parsed_tagarg *t_arg, *tt; + int amp_len; + + t = str; + while (*str) { + prev_status = status; + len += next_status(*str, &status); + if (status == R_ST_TAG0) { + Strclear(tagbuf); + Strcat_char(tagbuf, *str); + } + else if (status == R_ST_TAG || status == R_ST_DQUOTE || status == R_ST_QUOTE || status == R_ST_EQL) { + Strcat_char(tagbuf, *str); + } + else if (status == R_ST_AMP) { + if (prev_status == R_ST_NORMAL) { + Strclear(tagbuf); + amp_len = 0; + } + else { + Strcat_char(tagbuf, *str); + len++; + amp_len++; + } + } + else if (status == R_ST_NORMAL && prev_status == R_ST_AMP) { + Strcat_char(tagbuf, *str); + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + len += strlen(t) - 1 - amp_len; + if (*r2 != '\0') { + str -= strlen(r2); + } + } + else if (status == R_ST_NORMAL && ST_IS_REAL_TAG(prev_status)) { + Strcat_char(tagbuf, *str); + if (TAG_IS(tagbuf->ptr, "<img", 4)) { + int anchor_len = 0; + t_arg = parse_tag(tagbuf->ptr + 5); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "src") == 0 && tt->value && anchor_len == 0) { + r2 = tt->value + strlen(tt->value) - 1; + while (tt->value < r2 && *r2 != '/') + r2--; + if (*r2 == '/') + r2++; + while (*r2 && *r2 != '.') { + r2++; + anchor_len++; + } + anchor_len += 2; + } + else if (strcasecmp(tt->arg, "alt") == 0 && tt->value) { + anchor_len = strlen(tt->value) + 1; + break; + } + } + len += anchor_len; + } + else if (TAG_IS(tagbuf->ptr, "<input", 6)) { + int width = 20; + int valuelen = 1; + int input_type = FORM_INPUT_TEXT; + t_arg = parse_tag(tagbuf->ptr + 7); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "type") == 0 && tt->value) { + input_type = formtype(tt->value); + } + else if (strcasecmp(tt->arg, "value") == 0 && tt->value) { + valuelen = strlen(tt->value); + } + else if (strcasecmp(tt->arg, "width") == 0 && tt->value) { + width = atoi(tt->value); + } + } + switch (input_type) { + case FORM_INPUT_TEXT: + case FORM_INPUT_FILE: + case FORM_INPUT_PASSWORD: + len += width + 2; + break; + case FORM_INPUT_SUBMIT: + case FORM_INPUT_RESET: + case FORM_INPUT_IMAGE: + case FORM_INPUT_BUTTON: + len += valuelen + 2; + break; + case FORM_INPUT_CHECKBOX: + case FORM_INPUT_RADIO: + len += 3; + } + } + else if (TAG_IS(tagbuf->ptr, "<textarea", 9)) { + int width = 20; + t_arg = parse_tag(tagbuf->ptr + 7); + for (tt = t_arg; tt; tt = tt->next) { + if (strcasecmp(tt->arg, "cols") == 0 && tt->value) { + width = atoi(tt->value); + } + } + len += width + 2; + } + else if (TAG_IS(tagbuf->ptr, "<option", 7)) + len += 3; + } + else if (*str == '\t') { + len--; + do { + len++; + } while ((visible_length_offset + len) % Tabstop != 0); + } + str++; + } + if (status == R_ST_AMP) { + r2 = tagbuf->ptr; + t = getescapecmd(&r2); + len += strlen(t) - 1 - amp_len; + if (*r2 != '\0') { + len += strlen(r2); + } + } + return len; +} + +int +maximum_visible_length(char *str) +{ + int maxlen, len; + char *p; + + for (p = str; *p && *p != '\t'; p++); + + visible_length_offset = 0; + maxlen = visible_length(str); + + if (*p == '\0') + return maxlen; + + for (visible_length_offset = 1; visible_length_offset < Tabstop; + visible_length_offset++) { + len = visible_length(str); + if (maxlen < len) { + maxlen = len; + break; + } + } + return maxlen; +} + +char * +align(char *str, int width, int mode) +{ + int i, l, l1, l2; + Str buf = Strnew(); + + if (str == NULL || *str == '\0') { + for (i = 0; i < width; i++) + Strcat_char(buf, ' '); + return buf->ptr; + } + l = width - visible_length(str); + switch (mode) { + case ALIGN_CENTER: + l1 = l / 2; + l2 = l - l1; + for (i = 0; i < l1; i++) + Strcat_char(buf, ' '); + Strcat_charp(buf, str); + for (i = 0; i < l2; i++) + Strcat_char(buf, ' '); + return buf->ptr; + case ALIGN_LEFT: + Strcat_charp(buf, str); + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + return buf->ptr; + case ALIGN_RIGHT: + for (i = 0; i < l; i++) + Strcat_char(buf, ' '); + Strcat_charp(buf, str); + return buf->ptr; + } + return Strnew_charp(str)->ptr; +} + +void +print_item(struct table *t, + int row, int col, int width, + Str buf) +{ + int alignment; + char *p; + + if (t->tabdata[row]) + p = popText(t->tabdata[row][col]); + else + p = NULL; + + if (p != NULL) { + check_row(t, row); + alignment = ALIGN_CENTER; + if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_LEFT) + alignment = ALIGN_LEFT; + else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_RIGHT) + alignment = ALIGN_RIGHT; + else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_CENTER) + alignment = ALIGN_CENTER; + Strcat_charp(buf, align(p, width, alignment)); + } + else + Strcat_charp(buf, align(NULL, width, ALIGN_CENTER)); +} + + +#define T_TOP 0 +#define T_MIDDLE 1 +#define T_BOTTOM 2 + +void +print_sep(struct table *t, + int row, int type, int maxcol, + Str buf) +{ + int forbid; + char **rulep; + int i, j, k, l, m; + +#if defined(__EMX__)&&!defined(JP_CHARSET) + if(CodePage==850){ + ruleB = ruleB850; + rule = rule850; + } +#endif + if (row >= 0) + check_row(t, row); + check_row(t, row + 1); + if ((type == T_TOP || type == T_BOTTOM) && t->border_mode == BORDER_THICK) { + rulep = ruleB; + } + else { + rulep = rule; + } + forbid = 1; + if (type == T_TOP) + forbid |= 2; + else if (type == T_BOTTOM) + forbid |= 8; + else if (t->tabattr[row + 1][0] & HTT_Y) { + forbid |= 4; + } + if (t->border_mode != BORDER_NOWIN) + Strcat_charp(buf, RULE(t->border_mode)[forbid]); + for (i = 0; i <= maxcol; i++) { + forbid = 10; + if (type != T_BOTTOM && (t->tabattr[row + 1][i] & HTT_Y)) { + if (t->tabattr[row + 1][i] & HTT_X) { + goto do_last_sep; + } + else { + for (k = row; k >= 0 && t->tabattr[k] && (t->tabattr[k][i] & HTT_Y); k--); + m = t->tabwidth[i] + 2 * t->cellpadding; + for (l = i + 1; l <= t->maxcol && (t->tabattr[row][l] & HTT_X); l++) + m += t->tabwidth[l] + t->cellspacing; + print_item(t, k, i, m, buf); + } + } + else { + for (j = 0; j < t->tabwidth[i] + 2 * t->cellpadding; j += RULE_WIDTH) { + Strcat_charp(buf, rulep[forbid]); + } + } + do_last_sep: + if (i < maxcol) { + forbid = 0; + if (type == T_TOP) + forbid |= 2; + else if (t->tabattr[row][i + 1] & HTT_X) { + forbid |= 2; + } + if (type == T_BOTTOM) + forbid |= 8; + else { + if (t->tabattr[row + 1][i + 1] & HTT_X) { + forbid |= 8; + } + if (t->tabattr[row + 1][i + 1] & HTT_Y) { + forbid |= 4; + } + if (t->tabattr[row + 1][i] & HTT_Y) { + forbid |= 1; + } + } + if (forbid != 15) /* forbid==15 means 'no rule at all' */ + Strcat_charp(buf, rulep[forbid]); + } + } + forbid = 4; + if (type == T_TOP) + forbid |= 2; + if (type == T_BOTTOM) + forbid |= 8; + if (t->tabattr[row + 1][maxcol] & HTT_Y) { + forbid |= 1; + } + if (t->border_mode != BORDER_NOWIN) + Strcat_charp(buf, RULE(t->border_mode)[forbid]); + Strcat_charp(buf, "<eol>"); +} + +static int +get_spec_cell_width(struct table *tbl, int row, int col) +{ + int i, w; + + w = tbl->tabwidth[col]; + for (i = col + 1; i <= tbl->maxcol; i++) { + check_row(tbl, row); + if (tbl->tabattr[row][i] & HTT_X) + w += tbl->tabwidth[i] + tbl->cellspacing; + else + break; + } + return w; +} + +void +do_refill(struct table *tbl, int row, int col) +{ + TextList *orgdata; + TextListItem *l; + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + + if (tbl->tabdata[row] == NULL || + tbl->tabdata[row][col] == NULL) + return; + orgdata = tbl->tabdata[row][col]; + tbl->tabdata[row][col] = newTextList(); + + init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tabdata[row][col], + tbl->tabwidth[col], 0); + h_env.limit = get_spec_cell_width(tbl, row, col); + for (l = orgdata->first; l != NULL; l = l->next) { + if (TAG_IS(l->ptr, "<dummy_table", 12)) { + struct parsed_tagarg *t_arg, *t; + int id = -1; + t_arg = parse_tag(l->ptr + 12); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "id") && t->value) { + id = atoi(t->value); + break; + } + } + if (id >= 0) { + int alignment; + TextListItem *ti; + save_fonteffect(&h_env, h_env.obuf); + flushline(&h_env, &obuf, 0, 0, h_env.limit); + + if (RB_GET_ALIGN(h_env.obuf) == RB_CENTER) + alignment = ALIGN_CENTER; + else if (RB_GET_ALIGN(h_env.obuf) == RB_RIGHT) + alignment = ALIGN_RIGHT; + else + alignment = ALIGN_LEFT; + + if (alignment == ALIGN_LEFT) { + appendTextList(h_env.buf, tbl->tables[id].buf); + } + else { + for (ti = tbl->tables[id].buf->first; ti != NULL; ti = ti->next) { + pushText(h_env.buf, align(ti->ptr, h_env.limit, alignment)); + } + } + restore_fonteffect(&h_env, h_env.obuf); + } + } + else + HTMLlineproc1(l->ptr, &h_env); + } + flushline(&h_env, &obuf, 0, 0, h_env.limit); +} + +static void +check_cell_width(short *tabwidth, short *cellwidth, + short *col, short *colspan, short maxcell, + char *index, int space, int dir) +{ + int i, j, k, bcol, ecol; + int swidth, width; + + for (k = 0; k <= maxcell; k++) { + j = index[k]; + if (cellwidth[j] <= 0) + continue; + bcol = col[j]; + ecol = bcol + colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) + swidth += tabwidth[i]; + + width = cellwidth[j] - (colspan[j] - 1) * space; + if (width > swidth) { + int w = (width - swidth) / colspan[j]; + int r = (width - swidth) % colspan[j]; + for (i = bcol; i < ecol; i++) + tabwidth[i] += w; + /* dir {0: horizontal, 1: vertical} */ + if (dir == 1 && r > 0) + r = colspan[j]; + for (i = 1; i <= r; i++) + tabwidth[ecol - i]++; + } + } +} + +void +check_minimum_width(struct table *t, short *tabwidth) +{ + int i; + struct table_cell *cell = &t->cell; + + for (i = 0; i <= t->maxcol; i++) { + if (tabwidth[i] < t->minimum_width[i]) + tabwidth[i] = t->minimum_width[i]; + } + + check_cell_width(tabwidth, cell->minimum_width, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); +} + +void +check_maximum_width(struct table *t) +{ + struct table_cell *cell = &t->cell; +#ifdef MATRIX + int i, j, bcol, ecol; + int swidth, width; + + cell->necell = 0; + for (j = 0; j <= cell->maxcell; j++) { + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) + swidth += t->tabwidth[i]; + + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + if (width > swidth) { + cell->eindex[cell->necell] = j; + cell->necell++; + } + } +#else /* not MATRIX */ + check_cell_width(t->tabwidth, cell->width, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + check_minimum_width(t, t->tabwidth); +#endif /* not MATRIX */ +} + + +#ifdef MATRIX +static int +recalc_width(int old, int delta, double rat) +{ + double w = rat * old; + double ww = (double) delta; + if (w > 0.) { + if (ww < 0.) + ww = 0.; + ww += 0.2; + } + else { + if (ww > 0.) + ww = 0.; + ww -= 1.0; + } + if (w > ww) + return (int) (ww / rat); + return old; +} + +static int +check_compressible_cell(struct table *t, MAT * minv, + short *newwidth, short *swidth, short *cwidth, + int totalwidth, + int icol, int icell, double sxx, int corr) +{ + struct table_cell *cell = &t->cell; + int i, j, k, m, bcol, ecol; + int delta, dmax, dmin, owidth; + double sxy; + + if (sxx < 10.) + return corr; + + if (icol >= 0) { + owidth = newwidth[icol]; + delta = newwidth[icol] - t->tabwidth[icol]; + bcol = icol; + ecol = bcol + 1; + } + else if (icell >= 0) { + owidth = swidth[icell]; + delta = swidth[icell] - cwidth[icell]; + bcol = cell->col[icell]; + ecol = bcol + cell->colspan[icell]; + } + else { + owidth = totalwidth; + delta = totalwidth; + bcol = 0; + ecol = t->maxcol + 1; + } + + dmin = delta; + dmax = 0; + for (k = 0; k <= cell->maxcell; k++) { + int bcol1, ecol1; + if (dmin <= 0) + return corr; + j = cell->index[k]; + if (icol < 0 && j == icell) + continue; + bcol1 = cell->col[j]; + ecol1 = bcol1 + cell->colspan[j]; + sxy = 0.; + for (m = bcol1; m < ecol1; m++) { + for (i = bcol; i < ecol; i++) + sxy += m_entry(minv, i, m); + } + if (fabs(delta * sxy / sxx) < 0.5) + continue; + if (sxy > 0.) + dmin = recalc_width(dmin, swidth[j] - cwidth[j], sxy / sxx); + else + dmax = recalc_width(dmax, swidth[j] - cwidth[j], sxy / sxx); + } + for (m = 0; m <= t->maxcol; m++) { + if (dmin <= 0) + return corr; + if (icol >= 0 && m == icol) + continue; + sxy = 0.; + for (i = bcol; i < ecol; i++) + sxy += m_entry(minv, i, m); + if (fabs(delta * sxy / sxx) < 0.5) + continue; + if (sxy > 0.) + dmin = recalc_width(dmin, newwidth[m] - t->tabwidth[m], sxy / sxx); + else + dmax = recalc_width(dmax, newwidth[m] - t->tabwidth[m], sxy / sxx); + } + if (dmax > 0 && dmin > dmax) + dmin = dmax; + if (dmin > 1) { + correct_table_matrix(t, bcol, ecol - bcol, owidth - dmin, 1.); + corr++; + } + return corr; +} + +#define MAX_ITERATION 3 +int +check_table_width(struct table *t, short *newwidth, MAT * minv, int itr) +{ + int i, j, k, m, bcol, ecol; + int corr = 0; + struct table_cell *cell = &t->cell; +#ifdef __GNUC__ + short orgwidth[t->maxcol + 1]; + short cwidth[cell->maxcell + 1], swidth[cell->maxcell + 1]; +#else /* __GNUC__ */ + short orgwidth[MAXCOL]; + short cwidth[MAXCELL], swidth[MAXCELL]; +#endif /* __GNUC__ */ + int twidth; + double sxy, *Sxx, stotal; + + twidth = 0; + stotal = 0.; + for (i = 0; i <= t->maxcol; i++) { + twidth += newwidth[i]; + stotal += m_entry(minv, i, i); + for (m = 0; m < i; m++) { + stotal += 2 * m_entry(minv, i, m); + } + } + + Sxx = NewAtom_N(double, cell->maxcell + 1); + for (k = 0; k <= cell->maxcell; k++) { + j = cell->index[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth[j] = 0; + for (i = bcol; i < ecol; i++) + swidth[j] += newwidth[i]; + cwidth[j] = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + Sxx[j] = 0.; + for (i = bcol; i < ecol; i++) { + Sxx[j] += m_entry(minv, i, i); + for (m = bcol; m <= ecol; m++) { + if (m < i) + Sxx[j] += 2 * m_entry(minv, i, m); + } + } + } + + /* compress table */ + corr = check_compressible_cell(t, minv, newwidth, swidth, cwidth, twidth, + -1, -1, stotal, corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + + /* compress multicolumn cell */ + for (k = cell->maxcell; k >= 0; k--) { + j = cell->index[k]; + corr = check_compressible_cell(t, minv, newwidth, swidth, cwidth, twidth, + -1, j, Sxx[j], corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + } + + /* compress single column cell */ + for (i = 0; i <= t->maxcol; i++) { + corr = check_compressible_cell(t, minv, newwidth, swidth, cwidth, twidth, + i, -1, m_entry(minv, i, i), corr); + if (itr < MAX_ITERATION && corr > 0) + return corr; + } + + + for (i = 0; i <= t->maxcol; i++) + orgwidth[i] = newwidth[i]; + + check_minimum_width(t, newwidth); + + for (i = 0; i <= t->maxcol; i++) { + double sx = sqrt(m_entry(minv, i, i)); + if (sx < 0.1) + continue; + if (orgwidth[i] < t->minimum_width[i] && + newwidth[i] == t->minimum_width[i]) { + double w = (sx > 0.5) ? 0.5 : sx * 0.2; + sxy = 0.; + for (m = 0; m <= t->maxcol; m++) { + if (m == i) + continue; + sxy += m_entry(minv, i, m); + } + if (sxy <= 0.) { + correct_table_matrix(t, i, 1, t->minimum_width[i], w); + corr++; + } + } + } + + for (k = 0; k <= cell->maxcell; k++) { + int nwidth = 0, mwidth; + double sx; + + j = cell->index[k]; + sx = sqrt(Sxx[j]); + if (sx < 0.1) + continue; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + for (i = bcol; i < ecol; i++) + nwidth += newwidth[i]; + mwidth = cell->minimum_width[j] - (cell->colspan[j] - 1) * t->cellspacing; + if (mwidth > swidth[j] && mwidth == nwidth) { + double w = (sx > 0.5) ? 0.5 : sx * 0.2; + + sxy = 0.; + for (i = bcol; i < ecol; i++) { + for (m = 0; m <= t->maxcol; m++) { + if (m >= bcol && m < ecol) + continue; + sxy += m_entry(minv, i, m); + } + } + if (sxy <= 0.) { + correct_table_matrix(t, bcol, cell->colspan[j], mwidth, w); + corr++; + } + } + } + + if (itr >= MAX_ITERATION) + return 0; + else + return corr; +} + +#else /* not MATRIX */ +void +set_table_width(struct table *t, short *newwidth, int maxwidth) +{ + int i, j, k, bcol, ecol; + struct table_cell *cell = &t->cell; + char *fixed; + int swidth, fwidth, width, nvar; + double s; + double *dwidth; + int try_again; + + fixed = NewAtom_N(char, t->maxcol + 1); + bzero(fixed, t->maxcol + 1); + dwidth = NewAtom_N(double, t->maxcol + 1); + + for (i = 0; i <= t->maxcol; i++) { + dwidth[i] = 0.0; + if (t->fixed_width[i] < 0) { + t->fixed_width[i] = -t->fixed_width[i] * maxwidth / 100; + } + if (t->fixed_width[i] > 0) { + newwidth[i] = t->fixed_width[i]; + fixed[i] = 1; + } + else + newwidth[i] = 0; + if (newwidth[i] < t->minimum_width[i]) + newwidth[i] = t->minimum_width[i]; + } + + for (k = 0; k <= cell->maxcell; k++) { + j = cell->index[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + + if (cell->fixed_width[j] < 0) + cell->fixed_width[j] = -cell->fixed_width[j] * maxwidth / 100; + + swidth = 0; + fwidth = 0; + nvar = 0; + for (i = bcol; i < ecol; i++) { + if (fixed[i]) { + fwidth += newwidth[i]; + } + else { + swidth += newwidth[i]; + nvar++; + } + } + width = max(cell->fixed_width[j], cell->minimum_width[j]) + - (cell->colspan[j] - 1) * t->cellspacing; + if (nvar > 0 && width > fwidth + swidth) { + s = 0.; + for (i = bcol; i < ecol; i++) { + if (!fixed[i]) + s += weight3(t->tabwidth[i]); + } + for (i = bcol; i < ecol; i++) { + if (!fixed[i]) + dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s; + else + dwidth[i] = (double) newwidth[i]; + } + dv2sv(dwidth, newwidth, cell->colspan[j]); + if (cell->fixed_width[j] > 0) { + for (i = bcol; i < ecol; i++) + fixed[i] = 1; + } + } + } + + do { + nvar = 0; + swidth = 0; + fwidth = 0; + for (i = 0; i <= t->maxcol; i++) { + if (fixed[i]) { + fwidth += newwidth[i]; + } + else { + swidth += newwidth[i]; + nvar++; + } + } + width = maxwidth - t->maxcol * t->cellspacing; + if (nvar == 0 || width <= fwidth + swidth) + break; + + s = 0.; + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) + s += weight3(t->tabwidth[i]); + } + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) + dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s; + else + dwidth[i] = (double) newwidth[i]; + } + dv2sv(dwidth, newwidth, t->maxcol + 1); + + try_again = 0; + for (i = 0; i <= t->maxcol; i++) { + if (!fixed[i]) { + if (newwidth[i] > t->tabwidth[i]) { + newwidth[i] = t->tabwidth[i]; + fixed[i] = 1; + try_again = 1; + } + else if (newwidth[i] < t->minimum_width[i]) { + newwidth[i] = t->minimum_width[i]; + fixed[i] = 1; + try_again = 1; + } + } + } + } while (try_again); +} +#endif /* not MATRIX */ + +void +check_table_height(struct table *t) +{ + int i, j, k; + struct { + short row[MAXCELL]; + short rowspan[MAXCELL]; + char index[MAXCELL]; + short maxcell; + short height[MAXCELL]; + } cell; + int space; + + cell.maxcell = -1; + + for (j = 0; j <= t->maxrow; j++) { + if (!t->tabattr[j]) + continue; + for (i = 0; i <= t->maxcol; i++) { + int t_dep, rowspan; + if (t->tabattr[j][i] & (HTT_X | HTT_Y)) + continue; + + if (t->tabdata[j][i] == NULL) + t_dep = 0; + else + t_dep = t->tabdata[j][i]->nitem; + + rowspan = table_rowspan(t, j, i); + if (rowspan > 1) { + int c = cell.maxcell + 1; + k = dsort_index(rowspan, cell.rowspan, + j, cell.row, t->maxrow + 1, + cell.index, c); + if (k <= cell.maxcell) { + int idx = cell.index[k]; + if (cell.row[idx] == j && + cell.rowspan[idx] == rowspan) + c = idx; + } + if (c > cell.maxcell && c < MAXCELL) { + cell.maxcell++; + cell.row[cell.maxcell] = j; + cell.rowspan[cell.maxcell] = rowspan; + cell.height[cell.maxcell] = 0; + if (cell.maxcell > k) + bcopy(cell.index + k, cell.index + k + 1, cell.maxcell - k); + cell.index[k] = cell.maxcell; + } + if (c <= cell.maxcell && c >= 0 && + cell.height[c] < t_dep) { + cell.height[c] = t_dep; + continue; + } + } + if (t->tabheight[j] < t_dep) + t->tabheight[j] = t_dep; + } + } + + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + space = 1; + break; + case BORDER_NONE: + space = 0; + } + check_cell_width(t->tabheight, cell.height, cell.row, cell.rowspan, + cell.maxcell, cell.index, space, 1); +} + +int +get_table_width(struct table *t, short *orgwidth, short *cellwidth, + int check_minimum) +{ +#ifdef __GNUC__ + short newwidth[t->maxcol + 1]; +#else /* not __GNUC__ */ + short newwidth[MAXCOL]; +#endif /* not __GNUC__ */ + int i; + int swidth; + struct table_cell *cell = &t->cell; + + for (i = 0; i <= t->maxcol; i++) + newwidth[i] = max(orgwidth[i], 0); + + check_cell_width(newwidth, cellwidth, cell->col, cell->colspan, + cell->maxcell, cell->index, t->cellspacing, 0); + if (check_minimum) + check_minimum_width(t, newwidth); + + swidth = 0; + for (i = 0; i <= t->maxcol; i++) { + swidth += newwidth[i]; + } + swidth += table_border_width(t); + return swidth; +} + +#define minimum_table_width(t)\ +(get_table_width(t,t->minimum_width,t->cell.minimum_width,0)) +#define maximum_table_width(t)\ + (get_table_width(t,t->tabwidth,t->cell.width,0)) +#define fixed_table_width(t)\ + (get_table_width(t,t->fixed_width,t->cell.fixed_width,1)) + +void +renderCoTable(struct table *tbl) +{ + struct readbuffer obuf; + struct html_feed_environ h_env; + struct environment envs[MAX_ENV_LEVEL]; + struct table *t; + int i, j, col, row, b_width; + int width, nwidth, indent, maxwidth; +#ifdef __GNUC__ + short newwidth[tbl->maxcol + 1]; +#else /* not __GNUC__ */ + short newwidth[MAXCOL]; +#endif /* not __GNUC__ */ + + for (i = 0; i <= tbl->maxcol; i++) + newwidth[i] = tbl->tabwidth[i]; + + for (i = 0; i < tbl->ntable; i++) { + t = tbl->tables[i].ptr; + col = tbl->tables[i].col; + row = tbl->tables[i].row; + indent = tbl->tables[i].indent; + + init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf, + tbl->tabwidth[col], indent); + nwidth = newwidth[col]; + check_row(tbl, row); + b_width = 0; + for (j = col + 1; j <= tbl->maxcol; j++) { + if (tbl->tabattr[row][j] & HTT_X) { + h_env.limit += tbl->tabwidth[j]; + nwidth += newwidth[j]; + b_width += tbl->cellspacing; + } + else + break; + } + h_env.limit += b_width; +#ifndef TABLE_EXPAND + if (t->total_width == 0) + maxwidth = h_env.limit - indent; + else if (t->total_width > 0) + maxwidth = t->total_width; + else + maxwidth = t->total_width = -t->total_width * + get_spec_cell_width(tbl, row, col) / 100; +#else + maxwidth = h_env.limit - indent; + if (t->total_width > 0) { + double r = ((double) maxwidth) / t->total_width; + struct table_cell *cell = &t->cell; + + for (j = 0; j <= t->maxcol; j++) { + if (t->fixed_width[j] > 0) + t->fixed_width[j] = (int) (r * t->fixed_width[j]); + } + for (j = 0; j <= cell->maxcell; j++) { + if (cell->fixed_width[j] > 0) + cell->fixed_width[j] = (int) (r * cell->fixed_width[j]); + } + t->total_width = maxwidth; + } +#endif /* TABLE_EXPAND */ + renderTable(t, maxwidth, &h_env); + width = t->total_width - b_width + indent; + if (width > nwidth) { + int cell = tbl->tables[i].cell; + if (cell < 0) { + newwidth[col] = width; + } + else { + int ecol = col + tbl->cell.colspan[cell]; + int w = (width - nwidth) / tbl->cell.colspan[cell]; + int r = (width - nwidth) % tbl->cell.colspan[cell]; + for (j = col; j < ecol; j++) + newwidth[j] += w; + for (j = 1; j <= r; j++) + newwidth[ecol - j]++; + } + } + t = NULL; + } + for (i = 0; i <= tbl->maxcol; i++) + tbl->tabwidth[i] = newwidth[i]; +} + +static void +make_caption(struct table *t, struct html_feed_environ *h_env) +{ + struct html_feed_environ henv; + struct readbuffer obuf; + struct environment envs[MAX_ENV_LEVEL]; + TextList *tl; + Str tmp; + + if (t->caption->length <= 0) + return; + + if (t->total_width <= 0) + t->total_width = h_env->limit; + + init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextList(), t->total_width, + h_env->envs[h_env->envc].indent); + HTMLlineproc1("<center>", &henv); + HTMLlineproc1(t->caption->ptr, &henv); + HTMLlineproc1("</center>", &henv); + + tl = henv.buf; + + if (tl->nitem > 0) { + TextListItem *ti; + tmp = Strnew_charp("<pre for_table>"); + for (ti = tl->first; ti != NULL; ti = ti->next) { + Strcat_charp(tmp, ti->ptr); + Strcat_char(tmp, '\n'); + } + Strcat_charp(tmp, "</pre>"); + HTMLlineproc1(tmp->ptr, h_env); + } +} + +void +renderTable(struct table *t, + int max_width, + struct html_feed_environ *h_env) +{ + int i, j, w, r, h; + Str renderbuf = Strnew(); + short new_tabwidth[MAXCOL]; +#ifdef MATRIX + int itr; + VEC *newwidth; + MAT *mat, *minv; + PERM *pivot; +#endif /* MATRIX */ + int maxheight = 0; + Str vrulea, vruleb, vrulec; +#ifdef ID_EXT + Str idtag; +#endif /* ID_EXT */ + + if (t->maxcol < 0) { + make_caption(t, h_env); + return; + } + + max_width -= table_border_width(t); + + if (max_width <= 0) + max_width = 1; + + check_maximum_width(t); + +#ifdef MATRIX + if (t->maxcol == 0) { + if (t->tabwidth[0] > max_width) + t->tabwidth[0] = max_width; + if (t->total_width > 0) + t->tabwidth[0] = max_width; + else if (t->fixed_width[0] > 0) + t->tabwidth[0] = t->fixed_width[0]; + if (t->tabwidth[0] < t->minimum_width[0]) + t->tabwidth[0] = t->minimum_width[0]; + } + else { + set_table_matrix(t, max_width); + + itr = 0; + mat = m_get(t->maxcol + 1, t->maxcol + 1); + pivot = px_get(t->maxcol + 1); + newwidth = v_get(t->maxcol + 1); + minv = m_get(t->maxcol + 1, t->maxcol + 1); + do { + m_copy(t->matrix, mat); + LUfactor(mat, pivot); + LUsolve(mat, pivot, t->vector, newwidth); + dv2sv(newwidth->ve, new_tabwidth, t->maxcol + 1); + LUinverse(mat, pivot, minv); +#ifdef TABLE_DEBUG + fprintf(stderr, "max_width=%d\n", max_width); + fprintf(stderr, "minimum : "); + for (i = 0; i <= t->maxcol; i++) + fprintf(stderr, "%2d ", t->minimum_width[i]); + fprintf(stderr, "\ndecided : "); + for (i = 0; i <= t->maxcol; i++) + fprintf(stderr, "%2d ", new_tabwidth[i]); + fprintf(stderr, "\n"); +#endif /* TABLE_DEBUG */ + itr++; + + } while (check_table_width(t, new_tabwidth, minv, itr)); + v_free(newwidth); + px_free(pivot); + m_free(mat); + m_free(minv); + m_free(t->matrix); + v_free(t->vector); + for (i = 0; i <= t->maxcol; i++) { + t->tabwidth[i] = new_tabwidth[i]; + } + } +#else /* not MATRIX */ + set_table_width(t, new_tabwidth, max_width); + for (i = 0; i <= t->maxcol; i++) { + t->tabwidth[i] = new_tabwidth[i]; + } +#endif /* not MATRIX */ + + renderCoTable(t); + check_minimum_width(t, t->tabwidth); + + t->total_width = 0; + for (i = 0; i <= t->maxcol; i++) { + if (t->border_mode != BORDER_NONE && t->tabwidth[i] % RULE_WIDTH == 1) + t->tabwidth[i]++; + t->total_width += t->tabwidth[i]; + } + + t->total_width += table_border_width(t); + + for (i = 0; i <= t->maxcol; i++) { + for (j = 0; j <= t->maxrow; j++) { + check_row(t, j); + if (t->tabattr[j][i] & HTT_Y) + continue; + do_refill(t, j, i); + } + } + + check_table_height(t); + + /* table output */ + make_caption(t, h_env); + + HTMLlineproc1("<pre for_table>", h_env); +#ifdef ID_EXT + if (t->id != NULL) { + idtag = Sprintf("<_id id=\"%s\">", (t->id)->ptr); + HTMLlineproc1(idtag->ptr, h_env); + } +#endif /* ID_EXT */ + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + renderbuf = Strnew(); + print_sep(t, -1, T_TOP, t->maxcol, renderbuf); + HTMLlineproc1(renderbuf->ptr, h_env); + maxheight += 1; + break; + } + vruleb = Strnew(); + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + vrulea = Strnew(); + vrulec = Strnew(); + Strcat_charp(vrulea, TK_VERTICALBAR(t->border_mode)); + for (i = 0; i < t->cellpadding; i++) { + Strcat_char(vrulea, ' '); + Strcat_char(vruleb, ' '); + Strcat_char(vrulec, ' '); + } + Strcat_charp(vrulec, TK_VERTICALBAR(t->border_mode)); + case BORDER_NOWIN: +#if defined(__EMX__)&&!defined(JP_CHARSET) + Strcat_charp(vruleb, CodePage==850?"�":TN_VERTICALBAR); +#else + Strcat_charp(vruleb, TN_VERTICALBAR); +#endif + for (i = 0; i < t->cellpadding; i++) + Strcat_char(vruleb, ' '); + break; + case BORDER_NONE: + for (i = 0; i < t->cellspacing; i++) + Strcat_char(vruleb, ' '); + } + + for (r = 0; r <= t->maxrow; r++) { + for (h = 0; h < t->tabheight[r]; h++) { + renderbuf = Strnew(); + if (t->border_mode == BORDER_THIN || t->border_mode == BORDER_THICK) + Strcat(renderbuf, vrulea); +#ifdef ID_EXT + if (t->tridvalue[r] != NULL && h == 0) { + idtag = Sprintf("<_id id=\"%s\">", (t->tridvalue[r])->ptr); + Strcat(renderbuf, idtag); + } +#endif /* ID_EXT */ + for (i = 0; i <= t->maxcol; i++) { + check_row(t, r); +#ifdef ID_EXT + if (t->tabidvalue[r][i] != NULL && h == 0) { + idtag = Sprintf("<_id id=\"%s\">", (t->tabidvalue[r][i])->ptr); + Strcat(renderbuf, idtag); + } +#endif /* ID_EXT */ + if (!(t->tabattr[r][i] & HTT_X)) { + w = t->tabwidth[i]; + for (j = i + 1; + j <= t->maxcol && (t->tabattr[r][j] & HTT_X); + j++) + w += t->tabwidth[j] + t->cellspacing; + if (t->tabattr[r][i] & HTT_Y) { + for (j = r - 1; + j >= 0 && t->tabattr[j] && (t->tabattr[j][i] & HTT_Y); + j--); + print_item(t, j, i, w, renderbuf); + } + else + print_item(t, r, i, w, renderbuf); + } + if (i < t->maxcol && !(t->tabattr[r][i + 1] & HTT_X)) + Strcat(renderbuf, vruleb); + } + switch (t->border_mode) { + case BORDER_THIN: + case BORDER_THICK: + Strcat(renderbuf, vrulec); + Strcat_charp(renderbuf, "<eol>"); + maxheight += 1; + break; + case BORDER_NONE: + case BORDER_NOWIN: + Strcat_charp(renderbuf, "<eol>"); + break; + } + HTMLlineproc1(renderbuf->ptr, h_env); + } + if (r < t->maxrow && t->border_mode != BORDER_NONE) { + renderbuf = Strnew(); + print_sep(t, r, T_MIDDLE, t->maxcol, renderbuf); + HTMLlineproc1(renderbuf->ptr, h_env); + } + maxheight += t->tabheight[r]; + } + if (t->border_mode == BORDER_THIN || t->border_mode == BORDER_THICK) { + renderbuf = Strnew(); + print_sep(t, t->maxrow, T_BOTTOM, t->maxcol, renderbuf); + HTMLlineproc1(renderbuf->ptr, h_env); + maxheight += 1; + } + if (maxheight == 0) + HTMLlineproc1(" <eol>", h_env); + HTMLlineproc1("</pre>", h_env); +} + + +struct table * +begin_table(int border, int spacing, int padding) +{ + struct table *t; + int mincell = minimum_cellspacing(border); + + t = newTable(); + t->row = t->col = -1; + t->maxcol = -1; + t->maxrow = -1; + t->border_mode = border; + t->flag = 0; + if (border == BORDER_NOWIN) + t->flag |= TBL_EXPAND_OK; + t->cellspacing = max(spacing, mincell); + switch (border) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + t->cellpadding = (t->cellspacing - mincell) / 2; + break; + case BORDER_NONE: + t->cellpadding = t->cellspacing - mincell; + } + + if (padding > t->cellpadding) + t->cellpadding = padding; + + switch (border) { + case BORDER_THIN: + case BORDER_THICK: + case BORDER_NOWIN: + t->cellspacing = 2 * t->cellpadding + mincell; + break; + case BORDER_NONE: + t->cellspacing = t->cellpadding + mincell; + } + return t; +} + +static void +check_minimum0(struct table *t, int min) +{ + int i, w, ww; + struct table_cell *cell; + + if (t->col < 0) + return; + if (t->tabwidth[t->col] < 0) + return; + check_row(t, t->row); + w = table_colspan(t, t->row, t->col); + min += t->indent; + if (w == 1) + ww = min; + else { + cell = &t->cell; + ww = 0; + if (cell->icell >= 0 && cell->minimum_width[cell->icell] < min) + cell->minimum_width[cell->icell] = min; + } + for (i = t->col; + i <= t->maxcol && (i == t->col || (t->tabattr[t->row][i] & HTT_X)); + i++) { + if (t->minimum_width[i] < ww) + t->minimum_width[i] = ww; + } +} + +static int +setwidth0(struct table *t, struct table_mode *mode) +{ + int w; + int width = t->tabcontentssize; + struct table_cell *cell = &t->cell; + + if (t->col < 0) + return -1; + if (t->tabwidth[t->col] < 0) + return -1; + check_row(t, t->row); + if (t->linfo.prev_spaces > 0) + width -= t->linfo.prev_spaces; + w = table_colspan(t, t->row, t->col); + if (w == 1) { + if (t->tabwidth[t->col] < width) + t->tabwidth[t->col] = width; + } + else if (cell->icell >= 0) { + if (cell->width[cell->icell] < width) + cell->width[cell->icell] = width; + } + return width; +} + +static void +setwidth(struct table *t, struct table_mode *mode) +{ + int width = setwidth0(t, mode); + if (width < 0) + return; +#ifdef NOWRAP + if (t->tabattr[t->row][t->col] & HTT_NOWRAP) + check_minimum0(t, width); +#endif /* NOWRAP */ + if (mode->pre_mode & (TBLM_NOBR | TBLM_PRE | TBLM_PRE_INT) && + mode->nobr_offset >= 0) + check_minimum0(t, width - mode->nobr_offset); +} + +static void +addcontentssize(struct table *t, int width) +{ + + if (t->col < 0) + return; + if (t->tabwidth[t->col] < 0) + return; + check_row(t, t->row); + t->tabcontentssize += width; +} + +static void +clearcontentssize(struct table *t, struct table_mode *mode) +{ + mode->nobr_offset = 0; + t->linfo.prev_spaces = -1; + t->linfo.prevchar = ' '; + t->tabcontentssize = 0; +} + +void +check_rowcol(struct table *tbl) +{ + if (!(tbl->flag & TBL_IN_ROW)) { + tbl->flag |= TBL_IN_ROW; + tbl->row++; + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + tbl->col = -1; + } + if (tbl->row == -1) + tbl->row = 0; + if (tbl->col == -1) + tbl->col = 0; + + for (;; tbl->row++) { + check_row(tbl, tbl->row); + for (; tbl->col < MAXCOL && + tbl->tabattr[tbl->row][tbl->col] & (HTT_X | HTT_Y); + tbl->col++); + if (tbl->col < MAXCOL) + break; + tbl->col = 0; + } + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + if (tbl->col > tbl->maxcol) + tbl->maxcol = tbl->col; + + tbl->flag |= TBL_IN_COL; +} + +int +skip_space(struct table *t, char *line, struct table_linfo *linfo, + int checkminimum) +{ + int skip = 0, s = linfo->prev_spaces; + Lineprop ctype = linfo->prev_ctype, prev_ctype; + char prevchar = linfo->prevchar; + int w = (linfo->prev_spaces == -1) ? 0 : linfo->length; + int min = 1; + + if (*line == '<' && line[strlen(line) - 1] == '>') { + if (checkminimum) + check_minimum0(t, visible_length(line)); + return 0; + } + + while (*line) { + char c = *line, *save = line; + int ec = '\0', len = 1, wlen = 1; + prev_ctype = ctype; + ctype = get_ctype(c, prev_ctype); + if (min < w) + min = w; + if (ctype == PC_ASCII && IS_SPACE(c)) { + w = 0; + s++; + } + else { + if (c == '&') { + ec = getescapechar(&line); + if (ec) { + c = ec; + if (IS_CNTRL(ec)) + ctype = PC_CTRL; + else + ctype = PC_ASCII; + len = strlen(conv_latin1(ec)); + wlen = line - save; + } + } + if (prevchar && is_boundary(prevchar, prev_ctype, c, ctype)) { + w = len; + } + else { + w += len; + } + if (s > 0) { +#ifdef JP_CHARSET + if (ctype == PC_KANJI1 && prev_ctype == PC_KANJI2) + skip += s; + else +#endif /* JP_CHARSET */ + skip += s - 1; + } + s = 0; + } + prevchar = c; + line = save + wlen; + } + if (s > 1) { + skip += s - 1; + linfo->prev_spaces = 1; + } + else { + linfo->prev_spaces = s; + } + linfo->prev_ctype = ctype; + linfo->prevchar = prevchar; + + if (checkminimum) { + if (min < w) + min = w; + linfo->length = w; + check_minimum0(t, min); + } + return skip; +} + +#define TAG_ACTION_NONE 0 +#define TAG_ACTION_FEED 1 +#define TAG_ACTION_TABLE 2 +#define TAG_ACTION_N_TABLE 3 + +static int +feed_table_tag(struct table *tbl, char *line, struct table_mode *mode, int width) +{ + int cmd; + char *s_line; + struct table_cell *cell = &tbl->cell; + struct parsed_tagarg *t_arg, *t; + int colspan, rowspan; + int col, prev_col; + int i, j, k, v, v0, w, id, status; + Str tok, tmp, anchor; + table_attr align; + + s_line = line; + cmd = gethtmlcmd(&s_line, &status); + + if (mode->pre_mode & TBLM_IGNORE) { + switch (cmd) { + case HTML_N_STYLE: + mode->pre_mode &= ~TBLM_STYLE; + case HTML_N_SCRIPT: + mode->pre_mode &= ~TBLM_SCRIPT; + default: + return TAG_ACTION_NONE; + } + } + +#ifdef MENU_SELECT + /* failsafe: a tag other than <option></option>and </select> in * + * <select> environment is regarded as the end of <select>. */ + if (mode->pre_mode & TBLM_INSELECT) { + switch (cmd) { + case HTML_TABLE: + case HTML_N_TABLE: + case HTML_TR: + case HTML_N_TR: + case HTML_TD: + case HTML_N_TD: + case HTML_TH: + case HTML_N_TH: + case HTML_THEAD: + case HTML_N_THEAD: + case HTML_TBODY: + case HTML_N_TBODY: + case HTML_TFOOT: + case HTML_N_TFOOT: + case HTML_COLGROUP: + case HTML_N_COLGROUP: + case HTML_COL: + mode->pre_mode &= ~TBLM_INSELECT; + tmp = process_n_select(); + feed_table1(tbl, tmp, mode, width); + } + } +#endif /* MENU_SELECT */ + + switch (cmd) { + case HTML_TABLE: + return TAG_ACTION_TABLE; + case HTML_N_TABLE: + return TAG_ACTION_N_TABLE; + case HTML_TR: + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->caption = 0; + mode->indent_level = 0; + mode->nobr_level = 0; + mode->mode_level = 0; + mode->pre_mode = 0; + tbl->col = -1; + tbl->row++; + tbl->flag |= TBL_IN_ROW; + tbl->indent = 0; + align = 0; + t_arg = parse_tag(line + 3); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "align") && t->value) { + if (!strcasecmp(t->value, "left")) + align = (HTT_LEFT | HTT_TRSET); + else if (!strcasecmp(t->value, "right")) + align = (HTT_RIGHT | HTT_TRSET); + else if (!strcasecmp(t->value, "center")) + align = (HTT_CENTER | HTT_TRSET); + } +#ifdef ID_EXT + if (!strcasecmp(t->arg, "id") && t->value) { + tbl->tridvalue[tbl->row] = Strnew_charp(t->value); + } +#endif /* ID_EXT */ + } + tbl->trattr = align; + break; + case HTML_TH: + case HTML_TD: + prev_col = tbl->col; + if (tbl->col >= 0 && tbl->tabcontentssize > 0) + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->caption = 0; + mode->indent_level = 0; + mode->nobr_level = 0; + mode->mode_level = 0; + mode->pre_mode = 0; + tbl->flag |= TBL_IN_COL; + tbl->linfo.prev_spaces = -1; + tbl->linfo.prevchar = ' '; + tbl->linfo.prev_ctype = PC_ASCII; + tbl->indent = 0; + if (tbl->row == -1) { + /* for broken HTML... */ + tbl->row = -1; + tbl->col = -1; + tbl->maxrow = tbl->row; + } + if (tbl->col == -1) { + if (!(tbl->flag & TBL_IN_ROW)) { + tbl->row++; + tbl->flag |= TBL_IN_ROW; + } + if (tbl->row > tbl->maxrow) + tbl->maxrow = tbl->row; + } + tbl->col++; + check_row(tbl, tbl->row); + while (tbl->tabattr[tbl->row][tbl->col]) { + tbl->col++; + } + if (tbl->col > MAXCOL - 1) { + tbl->col = prev_col; + return TAG_ACTION_NONE; + } + if (tbl->col > tbl->maxcol) { + tbl->maxcol = tbl->col; + } + tbl->height = 0; + t_arg = parse_tag(line + 3); + colspan = rowspan = 1; + v = 0; + if (tbl->trattr & HTT_TRSET) + align = (tbl->trattr & HTT_ALIGN); + else if (cmd == HTML_TH) + align = HTT_CENTER; + else + align = HTT_LEFT; + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "rowspan") && t->value) { + rowspan = atoi(t->value); + if ((tbl->row + rowspan) >= tbl->max_rowsize) + check_row(tbl, tbl->row + rowspan); + } + else if (!strcasecmp(t->arg, "colspan") && t->value) { + colspan = atoi(t->value); + if ((tbl->col + colspan) >= MAXCOL) { + /* Can't expand column */ + colspan = MAXCOL - tbl->col; + } + } + else if (!strcasecmp(t->arg, "align") && t->value) { + if (!strcasecmp(t->value, "left")) + align = HTT_LEFT; + else if (!strcasecmp(t->value, "right")) + align = HTT_RIGHT; + else if (!strcasecmp(t->value, "center")) + align = HTT_CENTER; + } +#ifdef NOWRAP + else if (!strcasecmp(t->arg, "nowrap")) + tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP; +#endif /* NOWRAP */ + else if (!strcasecmp(t->arg, "width") && t->value) { + if (IS_DIGIT(*t->value)) { + v = atoi(t->value); + if (v == 0) + v = 1; + else if (v < 0) + v = 0; + if (t->value[strlen(t->value) - 1] == '%') { + v = -v; + } + else { +#ifdef TABLE_EXPAND + v = max(v / tbl->ppc, 1); +#else /* not TABLE_EXPAND */ + v = v / PIXEL_PER_CHAR; +#endif /* not TABLE_EXPAND */ + } + } + else + continue; +#ifdef ID_EXT + } + else if (!strcasecmp(t->arg, "id") && t->value) { + tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(t->value); +#endif /* ID_EXT */ + } + } +#ifdef NOWRAP + if (v != 0) { + /* NOWRAP and WIDTH= conflicts each other */ + tbl->tabattr[tbl->row][tbl->col] &= ~HTT_NOWRAP; + } +#endif /* NOWRAP */ + tbl->tabattr[tbl->row][tbl->col] &= ~HTT_ALIGN; + tbl->tabattr[tbl->row][tbl->col] |= align; + if (colspan > 1) { + col = tbl->col; + + cell->icell = cell->maxcell + 1; + k = dsort_index(colspan, cell->colspan, col, cell->col, MAXCOL, + cell->index, cell->icell); + if (k <= cell->maxcell) { + i = cell->index[k]; + if (cell->col[i] == col && + cell->colspan[i] == colspan) + cell->icell = i; + } + if (cell->icell > cell->maxcell && cell->icell < MAXCELL) { + cell->maxcell++; + cell->col[cell->maxcell] = col; + cell->colspan[cell->maxcell] = colspan; + cell->width[cell->maxcell] = 0; + cell->minimum_width[cell->maxcell] = 0; + cell->fixed_width[cell->maxcell] = 0; + if (cell->maxcell > k) + bcopy(cell->index + k, cell->index + k + 1, cell->maxcell - k); + cell->index[k] = cell->maxcell; + } + if (cell->icell > cell->maxcell) + cell->icell = -1; + } + if (v != 0) { + if (colspan == 1) { + v0 = tbl->fixed_width[tbl->col]; + if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) { +#ifdef TABLE_DEBUG + fprintf(stderr, "width(%d) = %d\n", tbl->col, v); +#endif /* TABLE_DEBUG */ + tbl->fixed_width[tbl->col] = v; + } + } + else if (cell->icell >= 0) { + v0 = cell->fixed_width[cell->icell]; + if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) + cell->fixed_width[cell->icell] = v; + } + } + for (i = 0; i < rowspan; i++) { + check_row(tbl, tbl->row + i); + for (j = 0; j < colspan; j++) { + tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y); + tbl->tabattr[tbl->row + i][tbl->col + j] |= + ((i > 0) ? HTT_Y : 0) | ((j > 0) ? HTT_X : 0); + if (tbl->col + j > tbl->maxcol) { + tbl->maxcol = tbl->col + j; + } + } + if (tbl->row + i > tbl->maxrow) { + tbl->maxrow = tbl->row + i; + } + } + break; + case HTML_N_TR: + setwidth(tbl, mode); + tbl->col = -1; + tbl->flag &= ~(TBL_IN_ROW | TBL_IN_COL); + return TAG_ACTION_NONE; + case HTML_N_TH: + case HTML_N_TD: + setwidth(tbl, mode); + tbl->flag &= ~TBL_IN_COL; +#ifdef TABLE_DEBUG + { + TextListItem *it; + int i = tbl->col, j = tbl->row; + fprintf(stderr, "(a) row,col: %d, %d\n", j, i); + if (tbl->tabdata[j] && tbl->tabdata[j][i]) { + for (it = tbl->tabdata[j][i]->first; it; it = it->next) + fprintf(stderr, " [%s] \n", it->ptr); + } + } +#endif + return TAG_ACTION_NONE; + case HTML_P: + case HTML_BR: + case HTML_DT: + case HTML_DD: + case HTML_CENTER: + case HTML_N_CENTER: + case HTML_DIV: + case HTML_N_DIV: + case HTML_H: + case HTML_N_H: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + if (cmd == HTML_DD) + addcontentssize(tbl, tbl->indent); + break; + case HTML_DL: + case HTML_BLQ: + case HTML_OL: + case HTML_UL: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->indent_level++; + if (mode->indent_level <= MAX_INDENT_LEVEL) + tbl->indent += INDENT_INCR; + if (cmd == HTML_BLQ) { + check_minimum0(tbl, 0); + addcontentssize(tbl, tbl->indent); + } + break; + case HTML_N_DL: + case HTML_N_BLQ: + case HTML_N_OL: + case HTML_N_UL: + if (mode->indent_level > 0) { + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + mode->indent_level--; + if (mode->indent_level < MAX_INDENT_LEVEL) + tbl->indent -= INDENT_INCR; + } + break; + case HTML_LI: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + check_minimum0(tbl, 0); + addcontentssize(tbl, tbl->indent); + break; + case HTML_PRE: + case HTML_LISTING: + case HTML_XMP: + setwidth(tbl, mode); + check_rowcol(tbl); + clearcontentssize(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + mode->pre_mode |= TBLM_PRE; + mode->mode_level++; + break; + case HTML_N_PRE: + case HTML_N_LISTING: + case HTML_N_XMP: + setwidth(tbl, mode); + check_rowcol(tbl); + clearcontentssize(tbl, mode); + pushdata(tbl, tbl->row, tbl->col, line); + if (mode->mode_level > 0) + mode->mode_level--; + if (mode->mode_level == 0) + mode->pre_mode &= ~TBLM_PRE; + tbl->linfo.prev_spaces = 0; + tbl->linfo.prevchar = '\0'; + tbl->linfo.prev_ctype = PC_ASCII; + break; + case HTML_NOBR: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (!(mode->pre_mode & TBLM_NOBR)) { + mode->nobr_offset = -1; + mode->pre_mode |= TBLM_NOBR; + } + mode->nobr_level++; + break; + case HTML_WBR: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + mode->nobr_offset = -1; + break; + case HTML_N_NOBR: + if (mode->nobr_level > 0) + mode->nobr_level--; + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (mode->nobr_level == 0) + mode->pre_mode &= ~TBLM_NOBR; + break; + case HTML_PRE_INT: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + if (!(mode->pre_mode & TBLM_PRE_INT)) { + mode->nobr_offset = -1; + mode->pre_mode |= TBLM_PRE_INT; + } + tbl->linfo.prev_spaces = 0; + break; + case HTML_N_PRE_INT: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + mode->pre_mode &= ~TBLM_PRE_INT; + break; + case HTML_IMG: + tok = process_img(parse_tag(line + 3)); + feed_table1(tbl, tok, mode, width); + break; +#ifdef NEW_FORM + case HTML_FORM: + process_form(parse_tag(line)); + break; + case HTML_N_FORM: + process_n_form(); + break; +#else /* not NEW_FORM */ + case HTML_FORM: + case HTML_N_FORM: + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, "<br>"); + setwidth(tbl, mode); + clearcontentssize(tbl, mode); + if (line[1] == '/') { + tok = Strnew_charp("</form_int "); + Strcat_charp(tok, line + 6); + } + else { + tok = Strnew_charp("<form_int "); + Strcat_charp(tok, line + 5); + } + pushdata(tbl, tbl->row, tbl->col, tok->ptr); + break; +#endif /* not NEW_FORM */ + case HTML_INPUT: + tmp = process_input(parse_tag(line + 6)); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_SELECT: + t_arg = parse_tag(line + 7); + tmp = process_select(t_arg); + feed_table1(tbl, tmp, mode, width); +#ifdef MENU_SELECT + if (!tag_exists(t_arg, "multiple")) /* non-multiple select */ + mode->pre_mode |= TBLM_INSELECT; +#endif /* MENU_SELECT */ + break; + case HTML_N_SELECT: +#ifdef MENU_SELECT + mode->pre_mode &= ~TBLM_INSELECT; +#endif /* MENU_SELECT */ + tmp = process_n_select(); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_OPTION: + tmp = process_option(parse_tag(line + 7)); + if (tmp) + feed_table1(tbl, tmp, mode, width); +#ifdef MENU_SELECT + else + feed_select(line); +#endif /* MENU_SELECT */ + break; + case HTML_TEXTAREA: + w = 0; + check_rowcol(tbl); + if (tbl->col + 1 <= tbl->maxcol && + tbl->tabattr[tbl->row][tbl->col + 1] & HTT_X) { + if (cell->icell >= 0 && cell->fixed_width[cell->icell] > 0) + w = cell->fixed_width[cell->icell]; + } + else { + if (tbl->fixed_width[tbl->col] > 0) + w = tbl->fixed_width[tbl->col]; + } + tmp = process_textarea(parse_tag(line + 9), w); + feed_table1(tbl, tmp, mode, width); + mode->pre_mode |= TBLM_INTXTA; + break; + case HTML_N_TEXTAREA: + mode->pre_mode &= ~TBLM_INTXTA; + tmp = process_n_textarea(); + feed_table1(tbl, tmp, mode, width); + break; + case HTML_A: + anchor = NULL; + check_rowcol(tbl); + t_arg = parse_tag(line + 2); + i = 0; + for (t = t_arg; t; t = t->next) { + if (strcasecmp(t->arg, "href") == 0 && t->value) { + anchor = Strnew_charp(t->value); + } + else if (strcasecmp(t->arg, "hseq") == 0 && t->value) { + i = atoi(t->value); + } + } + if (i == 0 && anchor) { + Str tmp = process_anchor(line); + pushdata(tbl, tbl->row, tbl->col, tmp->ptr); + if (mode->pre_mode & (TBLM_PRE | TBLM_NOBR)) + addcontentssize(tbl, 1); + else + check_minimum0(tbl, 1); + setwidth(tbl, mode); + } + else { + pushdata(tbl, tbl->row, tbl->col, line); + } + break; + case HTML_DEL: + case HTML_N_DEL: + case HTML_INS: + case HTML_N_INS: + pushdata(tbl, tbl->row, tbl->col, line); + i = 5; + check_minimum0(tbl, i); + addcontentssize(tbl, i); + setwidth(tbl, mode); + break; + case HTML_DUMMY_TABLE: + id = -1; + w = 0; + t_arg = parse_tag(line + 12); + for (t = t_arg; t; t = t->next) { + if (!strcasecmp(t->arg, "id") && t->value) { + id = atoi(t->value); + } + else if (!strcasecmp(t->arg, "width") && t->value) { + w = atoi(t->value); + } + } + if (id >= 0) { + setwidth(tbl, mode); + check_rowcol(tbl); + pushdata(tbl, tbl->row, tbl->col, line); + clearcontentssize(tbl, mode); + addcontentssize(tbl, maximum_table_width(tbl->tables[id].ptr)); + check_minimum0(tbl, minimum_table_width(tbl->tables[id].ptr)); + if (w > 0) + check_minimum0(tbl, w); + else + check_minimum0(tbl, fixed_table_width(tbl->tables[id].ptr)); + setwidth0(tbl, mode); + clearcontentssize(tbl, mode); + } + break; + case HTML_CAPTION: + mode->caption = 1; + break; + case HTML_N_CAPTION: + mode->caption = 0; + break; + case HTML_THEAD: + case HTML_N_THEAD: + case HTML_TBODY: + case HTML_N_TBODY: + case HTML_TFOOT: + case HTML_N_TFOOT: + case HTML_COLGROUP: + case HTML_N_COLGROUP: + case HTML_COL: + break; + case HTML_SCRIPT: + mode->pre_mode |= TBLM_SCRIPT; + break; + case HTML_STYLE: + mode->pre_mode |= TBLM_STYLE; + break; + default: + /* unknown tag: put into table */ + return TAG_ACTION_FEED; + } + return TAG_ACTION_NONE; +} + + +int +feed_table(struct table *tbl, char **tline, struct table_mode *mode, int width) +{ + int i; + char *line; + char *p; + Str tok, tmp; + struct table_linfo *linfo = &tbl->linfo; + + if (*tline == NULL || **tline == '\0') + return -1; + + tok = Strnew(); + if (tbl->suspended_input->length > 0) { + Strcat_charp(tbl->suspended_input, *tline); + *tline = tbl->suspended_input->ptr; + tbl->suspended_input = Strnew(); + tbl->status = R_ST_NORMAL; + } + while (read_token(tok, tline, &tbl->status, mode->pre_mode & TBLM_PREMODE, 0)) { + if (ST_IS_COMMENT(tbl->status)) { + continue; + } + else if (tbl->status != R_ST_NORMAL) { + /* line ended within tag */ + int l = tok->length; + if (tok->ptr[l - 1] == '\n') { + Strchop(tok); + if (ST_IS_REAL_TAG(tbl->status)) + Strcat_char(tok, ' '); + } + tbl->suspended_input = Strdup(tok); + return -1; + } + line = tok->ptr; + if (mode->caption && !TAG_IS(line, "</caption", 9)) { + Strcat(tbl->caption, tok); + continue; + } + if (*line == '<') { + int action = feed_table_tag(tbl, line, mode, width); + if (action == TAG_ACTION_NONE) + continue; + else if (action == TAG_ACTION_N_TABLE) + return 0; + else if (action == TAG_ACTION_TABLE) { + *tline -= tok->length; + return 1; + } + } + if (mode->pre_mode & TBLM_IGNORE) + continue; + if (mode->pre_mode & TBLM_INTXTA) { + feed_textarea(line); + continue; + } +#ifdef MENU_SELECT + if (mode->pre_mode & TBLM_INSELECT) { + feed_select(line); + continue; + } +#endif /* MENU_SELECT */ + /* convert &...; if it is latin-1 character */ + if (!(*line == '<' && line[strlen(line) - 1] == '>') && + strchr(line, '&') != NULL) { + tmp = Strnew(); + for (p = line; *p;) { + char *q, *r; + if (*p == '&') { + if (!strncasecmp(p, "&", 5) || + !strncasecmp(p, ">", 4) || + !strncasecmp(p, "<", 4)) { + /* do not convert */ + Strcat_char(tmp, *p); + p++; + } + else { + q = p; + r = getescapecmd(&p); + if (r != NULL && ((*r & 0x80) || IS_CNTRL(*r))) { + /* latin-1 character */ + Strcat_charp(tmp, r); + } + else { + Strcat_char(tmp, *q); + p = q + 1; + } + } + } + else { + Strcat_char(tmp, *p); + p++; + } + } + line = tmp->ptr; + } + if (!(mode->pre_mode & (TBLM_PRE | TBLM_PRE_INT))) { + if (!(tbl->flag & TBL_IN_COL)) { + linfo->prev_spaces = -1; + linfo->prev_ctype = PC_ASCII; + } + if (linfo->prev_spaces != 0) + while (IS_SPACE(*line)) + line++; + if (*line == '\0') + continue; + check_rowcol(tbl); + if (mode->pre_mode & TBLM_NOBR && mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + + /* count of number of spaces skipped in normal mode */ + i = skip_space(tbl, line, linfo, !(mode->pre_mode & TBLM_NOBR)); + addcontentssize(tbl, visible_length(line) - i); + setwidth(tbl, mode); + } + else { + /* <pre> mode or something like it */ + check_rowcol(tbl); + if (mode->pre_mode & TBLM_PRE_INT && mode->nobr_offset < 0) + mode->nobr_offset = tbl->tabcontentssize; + i = maximum_visible_length(line); + addcontentssize(tbl, i); + setwidth(tbl, mode); + if (!(mode->pre_mode & TBLM_PRE_INT)) { + p = line + strlen(line) - 1; + if (*p == '\r' || *p == '\n') + clearcontentssize(tbl, mode); + } + } + pushdata(tbl, tbl->row, tbl->col, line); + } + if (ST_IS_COMMENT(tbl->status)) + return 2; + else + return -1; +} + +void +feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width) +{ + char *p; + if (!tok) + return; + p = tok->ptr; + feed_table(tbl, &p, mode, width); +} + +void +pushTable(struct table *tbl, struct table *tbl1) +{ + int col; + int row; + + check_rowcol(tbl); + col = tbl->col; + row = tbl->row; + + if (tbl->ntable >= tbl->tables_size) { + struct table_in *tmp; + tbl->tables_size += MAX_TABLE_N; + tmp = New_N(struct table_in, tbl->tables_size); + if (tbl->tables) +#ifdef __CYGWIN__ + bcopy((const char *) tbl->tables, (char *) tmp, (size_t) tbl->ntable * sizeof(struct table_in)); +#else /* not __CYGWIN__ */ + bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in)); +#endif /* not __CYGWIN__ */ + tbl->tables = tmp; + } + + tbl->tables[tbl->ntable].ptr = tbl1; + tbl->tables[tbl->ntable].col = col; + tbl->tables[tbl->ntable].row = row; + tbl->tables[tbl->ntable].indent = tbl->indent; + tbl->tables[tbl->ntable].buf = newTextList(); + check_row(tbl, row); + if (col + 1 <= tbl->maxcol && + tbl->tabattr[row][col + 1] & HTT_X) + tbl->tables[tbl->ntable].cell = tbl->cell.icell; + else + tbl->tables[tbl->ntable].cell = -1; + tbl->ntable++; +} + +#ifdef MATRIX +int +correct_table_matrix(struct table *t, int col, int cspan, int a, double b) +{ + int i, j; + int ecol = col + cspan; + double w = 1. / (b * b); + + for (i = col; i < ecol; i++) { + v_add_val(t->vector, i, w * a); + for (j = i; j < ecol; j++) { + m_add_val(t->matrix, i, j, w); + m_set_val(t->matrix, j, i, m_entry(t->matrix, i, j)); + } + } + return i; +} + +static void +correct_table_matrix2(struct table *t, int col, int cspan, double s, double b) +{ + int i, j; + int ecol = col + cspan; + int size = t->maxcol + 1; + double w = 1. / (b * b); + double ss; + + for (i = 0; i < size; i++) { + for (j = i; j < size; j++) { + if (i >= col && i < ecol && j >= col && j < ecol) + ss = (1. - s) * (1. - s); + else if ((i >= col && i < ecol) || (j >= col && j < ecol)) + ss = -(1. - s) * s; + else + ss = s * s; + m_add_val(t->matrix, i, j, w * ss); + } + } +} + +static void +correct_table_matrix3(struct table *t, int col, char *flags, double s, double b) +{ + int i, j; + double ss; + int size = t->maxcol + 1; + double w = 1. / (b * b); + int flg = (flags[col] == 0); + + for (i = 0; i < size; i++) { + if (!((flg && flags[i] == 0) || (!flg && flags[i] != 0))) + continue; + for (j = i; j < size; j++) { + if (!((flg && flags[j] == 0) || (!flg && flags[j] != 0))) + continue; + if (i == col && j == col) + ss = (1. - s) * (1. - s); + else if (i == col || j == col) + ss = -(1. - s) * s; + else + ss = s * s; + m_add_val(t->matrix, i, j, w * ss); + } + } +} + +static void +set_table_matrix0(struct table *t, int maxwidth) +{ + int size = t->maxcol + 1; + int i, j, k, bcol, ecol; + int swidth, width, a; + double w0, w1, w, s, b; +#ifdef __GNUC__ + double we[size]; + char expand[size]; +#else /* not __GNUC__ */ + double we[MAXCOL]; + char expand[MAXCOL]; +#endif /* not __GNUC__ */ + struct table_cell *cell = &t->cell; + + w0 = 0.; + for (i = 0; i < size; i++) { + we[i] = weight(t->tabwidth[i]); + w0 += we[i]; + } + if (w0 <= 0.) + w0 = 1.; + + if (cell->necell == 0) { + for (i = 0; i < size; i++) { + s = we[i] / w0; + b = sigma_td_nw((int) (s * maxwidth)); + correct_table_matrix2(t, i, 1, s, b); + } + return; + } + + bzero(expand, size); + + for (k = 0; k < cell->necell; k++) { + j = cell->eindex[k]; + bcol = cell->col[j]; + ecol = bcol + cell->colspan[j]; + swidth = 0; + for (i = bcol; i < ecol; i++) { + swidth += t->tabwidth[i]; + expand[i]++; + } + width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing; + w = weight(width); + w1 = 0.; + for (i = bcol; i < ecol; i++) + w1 += we[i]; + s = w / (w0 + w - w1); + a = (int) (s * maxwidth); + b = sigma_td_nw(a); + correct_table_matrix2(t, bcol, cell->colspan[j], s, b); + } + + w1 = 0.; + for (i = 0; i < size; i++) + if (expand[i] == 0) + w1 += we[i]; + for (i = 0; i < size; i++) { + if (expand[i] == 0) { + s = we[i] / max(w1, 1.); + b = sigma_td_nw((int) (s * maxwidth)); + } + else { + s = we[i] / max(w0 - w1, 1.); + b = sigma_td_nw(maxwidth); + } + correct_table_matrix3(t, i, expand, s, b); + } +} + +void +set_table_matrix(struct table *t, int width) +{ + int size = t->maxcol + 1; + int i, j; + double b, s; + int a; + struct table_cell *cell = &t->cell; + + if (size < 1) + return; + + t->matrix = m_get(size, size); + t->vector = v_get(size); + for (i = 0; i < size; i++) { + for (j = i; j < size; j++) + m_set_val(t->matrix, i, j, 0.); + v_set_val(t->vector, i, 0.); + } + + for (i = 0; i < size; i++) { + if (t->fixed_width[i] > 0) { + a = max(t->fixed_width[i], t->minimum_width[i]); + b = sigma_td(a); + correct_table_matrix(t, i, 1, a, b); + } + else if (t->fixed_width[i] < 0) { + s = -(double) t->fixed_width[i] / 100.; + b = sigma_td((int) (s * width)); + correct_table_matrix2(t, i, 1, s, b); + } + } + + for (j = 0; j <= cell->maxcell; j++) { + if (cell->fixed_width[j] > 0) { + a = max(cell->fixed_width[j], cell->minimum_width[j]); + b = sigma_td(a); + correct_table_matrix(t, cell->col[j], + cell->colspan[j], a, b); + } + else if (cell->fixed_width[j] < 0) { + s = -(double) cell->fixed_width[j] / 100.; + b = sigma_td((int) (s * width)); + correct_table_matrix2(t, cell->col[j], + cell->colspan[j], s, b); + } + } + + set_table_matrix0(t, width); + + if (t->total_width > 0) { + b = sigma_table(width); + } + else { + b = sigma_table_nw(width); + } + correct_table_matrix(t, 0, size, width, b); +} +#endif /* MATRIX */ @@ -0,0 +1,147 @@ +/* $Id: table.h,v 1.1 2001/11/08 05:15:40 a-ito Exp $ */ +#if (defined(MESCHACH) && !defined(MATRIX)) +#define MATRIX +#endif /* (defined(MESCHACH) && !defined(MATRIX)) + * + * * * * * * */ + +#ifdef MATRIX +#ifdef MESCHACH +#include <matrix2.h> +#else /* not MESCHACH */ +#include "matrix.h" +#endif /* not MESCHACH */ +#endif /* MATRIX */ + +#include "Str.h" + +#define MAX_TABLE 20 /* maximum nest level of table */ +#define MAX_TABLE_N 20 /* maximum number of table in same level */ + +#define MAXROW 50 +#define MAXCOL 50 + +#define MAX_WIDTH 80 + +#define BORDER_NONE 0 +#define BORDER_THIN 1 +#define BORDER_THICK 2 +#define BORDER_NOWIN 3 + +typedef unsigned short table_attr; + +/* flag */ +#define TBL_IN_ROW 1 +#define TBL_EXPAND_OK 2 +#define TBL_IN_COL 4 + +#define MAXCELL 20 +struct table_cell { + short col[MAXCELL]; + short colspan[MAXCELL]; + char index[MAXCELL]; + short maxcell; + short icell; +#ifdef MATRIX + char eindex[MAXCELL]; + short necell; +#endif /* MATRIX */ + short width[MAXCELL]; + short minimum_width[MAXCELL]; + short fixed_width[MAXCELL]; +}; + +struct table_in { + struct table *ptr; + short col; + short row; + short cell; + short indent; + TextLineList *buf; +}; + +struct table_linfo { + Lineprop prev_ctype; + signed char prev_spaces; + int prevchar; + short length; +}; + +struct table { + int row; + int col; + int maxrow; + int maxcol; + int max_rowsize; + int border_mode; + int total_width; + int total_height; + int tabcontentssize; + int indent; + int cellspacing; + int cellpadding; + int vcellpadding; + int vspace; + int flag; +#ifdef TABLE_EXPAND + int real_width; +#endif /* TABLE_EXPAND */ + Str caption; +#ifdef ID_EXT + Str id; +#endif + GeneralList ***tabdata; + table_attr **tabattr; + table_attr trattr; +#ifdef ID_EXT + Str **tabidvalue; + Str *tridvalue; +#endif + short tabwidth[MAXCOL]; + short minimum_width[MAXCOL]; + short fixed_width[MAXCOL]; + struct table_cell cell; + short *tabheight; + struct table_in *tables; + short ntable; + short tables_size; + TextList *suspended_data; +/* use for counting skipped spaces */ + struct table_linfo linfo; +#ifdef MATRIX + MAT *matrix; + VEC *vector; +#endif /* MATRIX */ + int sloppy_width; +}; + +#define TBLM_PRE 1 +#define TBLM_NOBR 2 +#define TBLM_XMP 4 +#define TBLM_LST 8 +#define TBLM_PRE_INT 32 +#define TBLM_INTXTA 64 +#define TBLM_INSELECT 128 +#define TBLM_PREMODE (TBLM_PRE|TBLM_INTXTA|TBLM_INSELECT|TBLM_XMP|TBLM_LST) +#define TBLM_SPECIAL (TBLM_PRE|TBLM_PRE_INT|TBLM_XMP|TBLM_LST) +#define TBLM_PLAIN (TBLM_XMP|TBLM_LST) +#define TBLM_SCRIPT 256 +#define TBLM_STYLE 512 +#define TBLM_IGNORE (TBLM_SCRIPT|TBLM_STYLE) +#define TBLM_ANCHOR 1024 + +#define uchar unsigned char +#define ushort unsigned short +struct table_mode { + ushort pre_mode; + char indent_level; + char caption; + short nobr_offset; + char nobr_level; + short anchor_offset; +}; + +/* Local Variables: */ +/* c-basic-offset: 4 */ +/* tab-width: 8 */ +/* End: */ diff --git a/tagtable.c b/tagtable.c new file mode 100644 index 0000000..a2ec8db --- /dev/null +++ b/tagtable.c @@ -0,0 +1,261 @@ +#include "hash.h" +#include <stdio.h> +#include "html.h" +static HashItem_si MyHashItem[] = { + /* 0 */ {"/form_int",HTML_N_FORM_INT,&MyHashItem[1]}, + /* 1 */ {"/kbd",HTML_NOP,&MyHashItem[2]}, + /* 2 */ {"dd",HTML_DD,&MyHashItem[3]}, + /* 3 */ {"/dir",HTML_N_UL,NULL}, + /* 4 */ {"/body",HTML_N_BODY,NULL}, + /* 5 */ {"base",HTML_BASE,NULL}, + /* 6 */ {"/div",HTML_N_DIV,NULL}, + /* 7 */ {"tbody",HTML_TBODY,&MyHashItem[8]}, + /* 8 */ {"meta",HTML_META,&MyHashItem[9]}, + /* 9 */ {"i",HTML_NOP,NULL}, + /* 10 */ {"/p",HTML_N_P,NULL}, + /* 11 */ {"input_alt",HTML_INPUT_ALT,&MyHashItem[12]}, + /* 12 */ {"dl",HTML_DL,NULL}, + /* 13 */ {"/tbody",HTML_N_TBODY,&MyHashItem[14]}, + /* 14 */ {"/s",HTML_N_DEL,NULL}, + /* 15 */ {"del",HTML_DEL,&MyHashItem[16]}, + /* 16 */ {"xmp",HTML_XMP,&MyHashItem[17]}, + /* 17 */ {"br",HTML_BR,NULL}, + /* 18 */ {"/u",HTML_N_U,&MyHashItem[19]}, + /* 19 */ {"em",HTML_EM,NULL}, + /* 20 */ {"title_alt",HTML_TITLE_ALT,&MyHashItem[21]}, + /* 21 */ {"caption",HTML_CAPTION,&MyHashItem[22]}, + /* 22 */ {"plaintext",HTML_PLAINTEXT,&MyHashItem[23]}, + /* 23 */ {"p",HTML_P,NULL}, + /* 24 */ {"blockquote",HTML_BLQ,&MyHashItem[25]}, + /* 25 */ {"menu",HTML_UL,NULL}, + /* 26 */ {"/colgroup",HTML_N_COLGROUP,&MyHashItem[27]}, + /* 27 */ {"dfn",HTML_NOP,NULL}, + /* 28 */ {"s",HTML_DEL,&MyHashItem[29]}, + /* 29 */ {"strong",HTML_EM,NULL}, + /* 30 */ {"dt",HTML_DT,NULL}, + /* 31 */ {"_rule",HTML_RULE,&MyHashItem[32]}, + /* 32 */ {"u",HTML_U,NULL}, + /* 33 */ {"/map",HTML_N_MAP,&MyHashItem[34]}, + /* 34 */ {"/frameset",HTML_N_FRAMESET,&MyHashItem[35]}, + /* 35 */ {"/ol",HTML_N_OL,NULL}, + /* 36 */ {"/td",HTML_N_TD,NULL}, + /* 37 */ {"/_rule",HTML_N_RULE,&MyHashItem[38]}, + /* 38 */ {"li",HTML_LI,NULL}, + /* 39 */ {"html",HTML_BODY,&MyHashItem[40]}, + /* 40 */ {"hr",HTML_HR,NULL}, + /* 41 */ {"/strong",HTML_N_EM,NULL}, + /* 42 */ {"/th",HTML_N_TH,&MyHashItem[43]}, + /* 43 */ {"option",HTML_OPTION,&MyHashItem[44]}, + /* 44 */ {"kbd",HTML_NOP,&MyHashItem[45]}, + /* 45 */ {"dir",HTML_UL,NULL}, + /* 46 */ {"col",HTML_COL,NULL}, + /* 47 */ {"/caption",HTML_N_CAPTION,&MyHashItem[48]}, + /* 48 */ {"div",HTML_DIV,NULL}, + /* 49 */ {"head",HTML_HEAD,&MyHashItem[50]}, + /* 50 */ {"ol",HTML_OL,&MyHashItem[51]}, + /* 51 */ {"/ul",HTML_N_UL,NULL}, + /* 52 */ {"/ins",HTML_N_INS,&MyHashItem[53]}, + /* 53 */ {"area",HTML_AREA,NULL}, + /* 54 */ {"td",HTML_TD,&MyHashItem[55]}, + /* 55 */ {"/option",HTML_N_OPTION,NULL}, + /* 56 */ {"eol",HTML_EOL,&MyHashItem[57]}, + /* 57 */ {"/tr",HTML_N_TR,&MyHashItem[58]}, + /* 58 */ {"nobr",HTML_NOBR,NULL}, + /* 59 */ {"img_alt",HTML_IMG_ALT,&MyHashItem[60]}, + /* 60 */ {"table_alt",HTML_TABLE_ALT,&MyHashItem[61]}, + /* 61 */ {"th",HTML_TH,&MyHashItem[62]}, + /* 62 */ {"script",HTML_SCRIPT,&MyHashItem[63]}, + /* 63 */ {"/tt",HTML_NOP,NULL}, + /* 64 */ {"code",HTML_NOP,NULL}, + /* 65 */ {"samp",HTML_NOP,NULL}, + /* 66 */ {"textarea",HTML_TEXTAREA,NULL}, + /* 67 */ {"table",HTML_TABLE,&MyHashItem[68]}, + /* 68 */ {"img",HTML_IMG,&MyHashItem[69]}, + /* 69 */ {"/blockquote",HTML_N_BLQ,NULL}, + /* 70 */ {"applet",HTML_APPLET,&MyHashItem[71]}, + /* 71 */ {"map",HTML_MAP,&MyHashItem[72]}, + /* 72 */ {"ul",HTML_UL,NULL}, + /* 73 */ {"/script",HTML_N_SCRIPT,&MyHashItem[74]}, + /* 74 */ {"center",HTML_CENTER,NULL}, + /* 75 */ {"/table",HTML_N_TABLE,&MyHashItem[76]}, + /* 76 */ {"cite",HTML_NOP,&MyHashItem[77]}, + /* 77 */ {"/h1",HTML_N_H,NULL}, + /* 78 */ {"tr",HTML_TR,&MyHashItem[79]}, + /* 79 */ {"/h2",HTML_N_H,NULL}, + /* 80 */ {"/h3",HTML_N_H,NULL}, + /* 81 */ {"pre_int",HTML_PRE_INT,&MyHashItem[82]}, + /* 82 */ {"/font",HTML_N_FONT,&MyHashItem[83]}, + /* 83 */ {"tt",HTML_NOP,&MyHashItem[84]}, + /* 84 */ {"/h4",HTML_N_H,NULL}, + /* 85 */ {"body",HTML_BODY,&MyHashItem[86]}, + /* 86 */ {"/form",HTML_N_FORM,&MyHashItem[87]}, + /* 87 */ {"/h5",HTML_N_H,NULL}, + /* 88 */ {"/h6",HTML_N_H,NULL}, + /* 89 */ {"frame",HTML_FRAME,NULL}, + /* 90 */ {"/img_alt",HTML_N_IMG_ALT,&MyHashItem[91]}, + /* 91 */ {"/center",HTML_N_CENTER,NULL}, + /* 92 */ {"/pre",HTML_N_PRE,NULL}, + /* 93 */ {"tfoot",HTML_TFOOT,NULL}, + /* 94 */ {"ins",HTML_INS,NULL}, + /* 95 */ {"/var",HTML_NOP,NULL}, + /* 96 */ {"h1",HTML_H,NULL}, + /* 97 */ {"/tfoot",HTML_N_TFOOT,&MyHashItem[98]}, + /* 98 */ {"input",HTML_INPUT,&MyHashItem[99]}, + /* 99 */ {"h2",HTML_H,NULL}, + /* 100 */ {"h3",HTML_H,NULL}, + /* 101 */ {"h4",HTML_H,NULL}, + /* 102 */ {"h5",HTML_H,NULL}, + /* 103 */ {"h6",HTML_H,NULL}, + /* 104 */ {"/pre_int",HTML_N_PRE_INT,NULL}, + /* 105 */ {"/menu",HTML_N_UL,NULL}, + /* 106 */ {"form_int",HTML_FORM_INT,NULL}, + /* 107 */ {"style",HTML_STYLE,&MyHashItem[108]}, + /* 108 */ {"address",HTML_BR,NULL}, + /* 109 */ {"/textarea",HTML_N_TEXTAREA,NULL}, + /* 110 */ {"/input_alt",HTML_N_INPUT_ALT,NULL}, + /* 111 */ {"doctype",HTML_DOCTYPE,&MyHashItem[112]}, + /* 112 */ {"/style",HTML_N_STYLE,NULL}, + /* 113 */ {"/html",HTML_N_BODY,NULL}, + /* 114 */ {"pre",HTML_PRE,&MyHashItem[115]}, + /* 115 */ {"title",HTML_TITLE,NULL}, + /* 116 */ {"select",HTML_SELECT,NULL}, + /* 117 */ {"var",HTML_NOP,NULL}, + /* 118 */ {"/title",HTML_N_TITLE,NULL}, + /* 119 */ {"embed",HTML_EMBED,&MyHashItem[120]}, + /* 120 */ {"colgroup",HTML_COLGROUP,&MyHashItem[121]}, + /* 121 */ {"/head",HTML_N_HEAD,&MyHashItem[122]}, + /* 122 */ {"isindex",HTML_ISINDEX,NULL}, + /* 123 */ {"strike",HTML_DEL,&MyHashItem[124]}, + /* 124 */ {"listing",HTML_LISTING,NULL}, + /* 125 */ {"bgsound",HTML_BGSOUND,NULL}, + /* 126 */ {"/address",HTML_BR,NULL}, + /* 127 */ {"thead",HTML_THEAD,&MyHashItem[128]}, + /* 128 */ {"wbr",HTML_WBR,&MyHashItem[129]}, + /* 129 */ {"/del",HTML_N_DEL,&MyHashItem[130]}, + /* 130 */ {"/nobr",HTML_N_NOBR,&MyHashItem[131]}, + /* 131 */ {"/select",HTML_N_SELECT,&MyHashItem[132]}, + /* 132 */ {"frameset",HTML_FRAMESET,&MyHashItem[133]}, + /* 133 */ {"/xmp",HTML_N_XMP,NULL}, + /* 134 */ {"/code",HTML_NOP,NULL}, + /* 135 */ {"/thead",HTML_N_THEAD,&MyHashItem[136]}, + /* 136 */ {"/samp",HTML_NOP,&MyHashItem[137]}, + /* 137 */ {"/dfn",HTML_NOP,&MyHashItem[138]}, + /* 138 */ {"_id",HTML_NOP,NULL}, + /* 139 */ {"/strike",HTML_N_DEL,&MyHashItem[140]}, + /* 140 */ {"/a",HTML_N_A,NULL}, + /* 141 */ {"/b",HTML_N_B,NULL}, + /* 142 */ {"font",HTML_FONT,&MyHashItem[143]}, + /* 143 */ {"/dl",HTML_N_DL,NULL}, + /* 144 */ {"form",HTML_FORM,&MyHashItem[145]}, + /* 145 */ {"/cite",HTML_NOP,&MyHashItem[146]}, + /* 146 */ {"a",HTML_A,NULL}, + /* 147 */ {"b",HTML_B,NULL}, + /* 148 */ {"/listing",HTML_N_LISTING,&MyHashItem[149]}, + /* 149 */ {"/em",HTML_N_EM,&MyHashItem[150]}, + /* 150 */ {"/i",HTML_NOP,NULL}, +}; + +static HashItem_si *MyHashItemTbl[] = { + &MyHashItem[0], + &MyHashItem[4], + NULL, + &MyHashItem[5], + &MyHashItem[6], + &MyHashItem[7], + &MyHashItem[10], + NULL, + &MyHashItem[11], + &MyHashItem[13], + &MyHashItem[15], + &MyHashItem[18], + &MyHashItem[20], + &MyHashItem[24], + &MyHashItem[26], + &MyHashItem[28], + &MyHashItem[30], + &MyHashItem[31], + &MyHashItem[33], + NULL, + &MyHashItem[36], + &MyHashItem[37], + &MyHashItem[39], + &MyHashItem[41], + &MyHashItem[42], + NULL, + &MyHashItem[46], + NULL, + &MyHashItem[47], + NULL, + &MyHashItem[49], + &MyHashItem[52], + &MyHashItem[54], + NULL, + &MyHashItem[56], + NULL, + &MyHashItem[59], + &MyHashItem[64], + &MyHashItem[65], + &MyHashItem[66], + NULL, + &MyHashItem[67], + &MyHashItem[70], + NULL, + &MyHashItem[73], + &MyHashItem[75], + &MyHashItem[78], + &MyHashItem[80], + &MyHashItem[81], + &MyHashItem[85], + &MyHashItem[88], + &MyHashItem[89], + &MyHashItem[90], + &MyHashItem[92], + &MyHashItem[93], + &MyHashItem[94], + &MyHashItem[95], + &MyHashItem[96], + &MyHashItem[97], + &MyHashItem[100], + &MyHashItem[101], + &MyHashItem[102], + &MyHashItem[103], + NULL, + &MyHashItem[104], + &MyHashItem[105], + NULL, + NULL, + &MyHashItem[106], + &MyHashItem[107], + NULL, + &MyHashItem[109], + &MyHashItem[110], + &MyHashItem[111], + &MyHashItem[113], + NULL, + NULL, + &MyHashItem[114], + &MyHashItem[116], + NULL, + &MyHashItem[117], + &MyHashItem[118], + &MyHashItem[119], + &MyHashItem[123], + &MyHashItem[125], + &MyHashItem[126], + &MyHashItem[127], + NULL, + NULL, + &MyHashItem[134], + &MyHashItem[135], + &MyHashItem[139], + &MyHashItem[141], + NULL, + NULL, + NULL, + &MyHashItem[142], + &MyHashItem[144], + &MyHashItem[147], + &MyHashItem[148], +}; + +Hash_si tagtable = {100, MyHashItemTbl}; diff --git a/tagtable.tab b/tagtable.tab new file mode 100644 index 0000000..8954857 --- /dev/null +++ b/tagtable.tab @@ -0,0 +1,154 @@ +#include <stdio.h> +#include "html.h" +%% +a HTML_A +/a HTML_N_A +_id HTML_NOP +hr HTML_HR +h1 HTML_H +h2 HTML_H +h3 HTML_H +h4 HTML_H +h5 HTML_H +h6 HTML_H +/h1 HTML_N_H +/h2 HTML_N_H +/h3 HTML_N_H +/h4 HTML_N_H +/h5 HTML_N_H +/h6 HTML_N_H +p HTML_P +br HTML_BR +b HTML_B +/b HTML_N_B +i HTML_NOP +/i HTML_NOP +tt HTML_NOP +/tt HTML_NOP +ul HTML_UL +/ul HTML_N_UL +menu HTML_UL +/menu HTML_N_UL +dir HTML_UL +/dir HTML_N_UL +li HTML_LI +ol HTML_OL +/ol HTML_N_OL +title HTML_TITLE +/title HTML_N_TITLE +dl HTML_DL +/dl HTML_N_DL +dt HTML_DT +dd HTML_DD +pre HTML_PRE +/pre HTML_N_PRE +blockquote HTML_BLQ +/blockquote HTML_N_BLQ +img HTML_IMG +code HTML_NOP +/code HTML_NOP +dfn HTML_NOP +/dfn HTML_NOP +em HTML_EM +/em HTML_N_EM +cite HTML_NOP +/cite HTML_NOP +kbd HTML_NOP +/kbd HTML_NOP +samp HTML_NOP +/samp HTML_NOP +strong HTML_EM +/strong HTML_N_EM +var HTML_NOP +/var HTML_NOP +address HTML_BR +/address HTML_BR +xmp HTML_XMP +/xmp HTML_N_XMP +listing HTML_LISTING +/listing HTML_N_LISTING +plaintext HTML_PLAINTEXT +table HTML_TABLE +/table HTML_N_TABLE +meta HTML_META +/p HTML_N_P +frame HTML_FRAME +frameset HTML_FRAMESET +/frameset HTML_N_FRAMESET +center HTML_CENTER +/center HTML_N_CENTER +font HTML_FONT +/font HTML_N_FONT +form HTML_FORM +/form HTML_N_FORM +input HTML_INPUT +textarea HTML_TEXTAREA +/textarea HTML_N_TEXTAREA +select HTML_SELECT +/select HTML_N_SELECT +option HTML_OPTION +/option HTML_N_OPTION +nobr HTML_NOBR +/nobr HTML_N_NOBR +div HTML_DIV +/div HTML_N_DIV +isindex HTML_ISINDEX +map HTML_MAP +/map HTML_N_MAP +area HTML_AREA +script HTML_SCRIPT +/script HTML_N_SCRIPT +base HTML_BASE +del HTML_DEL +/del HTML_N_DEL +strike HTML_DEL +/strike HTML_N_DEL +s HTML_DEL +/s HTML_N_DEL +ins HTML_INS +/ins HTML_N_INS +u HTML_U +/u HTML_N_U +style HTML_STYLE +/style HTML_N_STYLE +wbr HTML_WBR +head HTML_HEAD +/head HTML_N_HEAD +body HTML_BODY +/body HTML_N_BODY +html HTML_BODY +/html HTML_N_BODY +doctype HTML_DOCTYPE +tr HTML_TR +/tr HTML_N_TR +td HTML_TD +/td HTML_N_TD +th HTML_TH +/th HTML_N_TH +caption HTML_CAPTION +/caption HTML_N_CAPTION +thead HTML_THEAD +/thead HTML_N_THEAD +tbody HTML_TBODY +/tbody HTML_N_TBODY +tfoot HTML_TFOOT +/tfoot HTML_N_TFOOT +colgroup HTML_COLGROUP +/colgroup HTML_N_COLGROUP +col HTML_COL +table_alt HTML_TABLE_ALT +_rule HTML_RULE +/_rule HTML_N_RULE +title_alt HTML_TITLE_ALT +form_int HTML_FORM_INT +/form_int HTML_N_FORM_INT +input_alt HTML_INPUT_ALT +/input_alt HTML_N_INPUT_ALT +img_alt HTML_IMG_ALT +/img_alt HTML_N_IMG_ALT +eol HTML_EOL +pre_int HTML_PRE_INT +/pre_int HTML_N_PRE_INT +bgsound HTML_BGSOUND +applet HTML_APPLET +embed HTML_EMBED @@ -0,0 +1,1839 @@ +/* $Id: terms.c,v 1.1 2001/11/08 05:15:43 a-ito Exp $ */ +/* + * An original curses library for EUC-kanji by Akinori ITO, December 1989 + * revised by Akinori ITO, January 1995 + */ +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/time.h> +#include <unistd.h> +#include "config.h" +#include <string.h> +#ifdef MOUSE +#ifdef USE_GPM +#include <gpm.h> +#endif /* USE_GPM */ +#ifdef USE_SYSMOUSE +#include <machine/console.h> +int (*sysm_handler) (int x, int y, int nbs, int obs); +static int cwidth = 8, cheight = 16; +static int xpix, ypix, nbs, obs = 0; +#endif /* use_SYSMOUSE */ + +static int is_xterm = 0; +void mouse_init(), mouse_end(); +int mouseActive = 0; +#endif /* MOUSE */ + +#ifdef AIX +#include <sys/select.h> +#endif /* AIX */ + +#include "terms.h" +#include "fm.h" +#include "myctype.h" + +#ifdef __EMX__ +#define INCL_DOSNLS +#include <os2.h> +#include <sys/select.h> + +#ifndef JP_CHARSET +extern int CodePage; +#endif /* !JP_CHARSET */ +#endif /* __EMX__ */ + +char *getenv(const char *); +MySignalHandler reset_exit(SIGNAL_ARG), error_dump(SIGNAL_ARG); +void setlinescols(void); +void flush_tty(); + +#ifndef SIGIOT +#define SIGIOT SIGABRT +#endif /* not SIGIOT */ + +#ifdef TERMIO +#include <sys/ioctl.h> +#include <termio.h> +typedef struct termio TerminalMode; +#define TerminalSet(fd,x) ioctl(fd,TCSETA,x) +#define TerminalGet(fd,x) ioctl(fd,TCGETA,x) +#define MODEFLAG(d) ((d).c_lflag) +#define IMODEFLAG(d) ((d).c_iflag) +#endif /* TERMIO */ + +#ifdef TERMIOS +#include <termios.h> +#include <unistd.h> +typedef struct termios TerminalMode; +#define TerminalSet(fd,x) tcsetattr(fd,TCSANOW,x) +#define TerminalGet(fd,x) tcgetattr(fd,x) +#define MODEFLAG(d) ((d).c_lflag) +#define IMODEFLAG(d) ((d).c_iflag) +#endif /* TERMIOS */ + +#ifdef SGTTY +#include <sys/ioctl.h> +#include <sgtty.h> +typedef struct sgttyb TerminalMode; +#define TerminalSet(fd,x) ioctl(fd,TIOCSETP,x) +#define TerminalGet(fd,x) ioctl(fd,TIOCGETP,x) +#define MODEFLAG(d) ((d).sg_flags) +#endif /* SGTTY */ + +#define MAX_LINE 200 +#define MAX_COLUMN 400 + +/* Screen properties */ +#define S_SCREENPROP 0x0f +#define S_NORMAL 0x00 +#define S_STANDOUT 0x01 +#define S_UNDERLINE 0x02 +#define S_BOLD 0x04 +#define S_EOL 0x08 + +/* Sort of Character */ +#define C_WHICHCHAR 0xc0 +#define C_ASCII 0x00 +#ifdef JP_CHARSET +#define C_WCHAR1 0x40 +#define C_WCHAR2 0x80 +#endif /* JP_CHARSET */ +#define C_CTRL 0xc0 + +#define CHMODE(c) ((c)&C_WHICHCHAR) +#define SETCHMODE(var,mode) var = (((var)&~C_WHICHCHAR) | mode) + +/* Charactor Color */ +#define COL_FCOLOR 0xf00 +#define COL_FBLACK 0x800 +#define COL_FRED 0x900 +#define COL_FGREEN 0xa00 +#define COL_FYELLOW 0xb00 +#define COL_FBLUE 0xc00 +#define COL_FMAGENTA 0xd00 +#define COL_FCYAN 0xe00 +#define COL_FWHITE 0xf00 +#define COL_FTERM 0x000 + +#define S_COLORED 0xf00 + +#ifdef BG_COLOR +/* Background Color */ +#define COL_BCOLOR 0xf000 +#define COL_BBLACK 0x8000 +#define COL_BRED 0x9000 +#define COL_BGREEN 0xa000 +#define COL_BYELLOW 0xb000 +#define COL_BBLUE 0xc000 +#define COL_BMAGENTA 0xd000 +#define COL_BCYAN 0xe000 +#define COL_BWHITE 0xf000 +#define COL_BTERM 0x0000 + +#define S_BCOLORED 0xf000 +#endif /* BG_COLOR */ + + +#define S_GRAPHICS 0x10 + +#define S_DIRTY 0x20 + +#define SETPROP(var,prop) (var = (((var)&S_DIRTY) | prop)) + +/* Line status */ +#define L_DIRTY 0x01 +#define L_UNUSED 0x02 +#define L_NEED_CE 0x04 +#define L_CLRTOEOL 0x08 + +#define ISDIRTY(d) ((d) & L_DIRTY) +#define ISUNUSED(d) ((d) & L_UNUSED) +#define NEED_CE(d) ((d) & L_NEED_CE) + +typedef unsigned short l_prop; + +typedef struct scline { + char *lineimage; + l_prop *lineprop; + short isdirty; + short eol; +} Screen; + +static +TerminalMode d_ioval; +static +int tty; +static +FILE *ttyf; + +static +char bp[1024], funcstr[256]; + +char *T_cd, *T_ce, *T_kr, *T_kl, *T_cr, *T_bt, *T_ta, *T_sc, *T_rc, +*T_so, *T_se, *T_us, *T_ue, *T_cl, *T_cm, *T_al, *T_sr, *T_md, *T_me, +*T_ti, *T_te, *T_nd, *T_as, *T_ae, *T_eA, *T_ac, *T_op; + +int LINES, COLS; +static int max_LINES = 0, max_COLS = 0; +static int tab_step = 8; +static int CurLine, CurColumn; +static Screen *ScreenElem = NULL, **ScreenImage = NULL; +static l_prop CurrentMode = 0; +static int graph_enabled = 0; + +char DisplayCode = DISPLAY_CODE; + +static char gcmap[96]; + +extern int tgetent(char *, char *); +extern int tgetnum(char *); +extern int tgetflag(char *); +extern char *tgetstr(char *, char **); +extern char *tgoto(char *, int, int); +extern int tputs(char *, int, int (*)(char)); +void putchars(unsigned char, unsigned char, FILE *); +void clear(), wrap(), touch_line(), touch_column(int); +#ifdef JP_CHARSET +void switch_wchar(FILE *); +void switch_ascii(FILE *); +#endif +void need_clrtoeol(void), clrtoeol(void); + +int write1(char); + +/* #define writestr(s) tputs(s,1,write1) */ + +static void +writestr(char *s) +{ + tputs(s, 1, write1); +} + +#define MOVE(line,column) writestr(tgoto(T_cm,column,line)); + +int +set_tty(void) +{ + char *ttyn; +#ifdef MOUSE + char *term; +#endif + + if (isatty(0)) /* stdin */ + ttyn = ttyname(0); + else +#ifndef __EMX__ + ttyn = "/dev/tty"; +#else /* __EMX__ */ + ttyn = "con"; +#endif /* __EMX__ */ + tty = open(ttyn, O_RDWR); + if (tty < 0) { + /* use stderr instead of stdin... is it OK???? */ + tty = 2; + } + ttyf = fdopen(tty, "w"); + TerminalGet(tty, &d_ioval); +#ifdef MOUSE + term = getenv("TERM"); + if (!strncmp(term, "kterm", 5) || !strncmp(term, "xterm", 5)) { + is_xterm = 1; + } +#endif + return 0; +} + +void +ttymode_set(int mode, int imode) +{ + int er; + TerminalMode ioval; + + TerminalGet(tty, &ioval); + MODEFLAG(ioval) |= mode; +#ifndef SGTTY + IMODEFLAG(ioval) |= imode; +#endif /* not SGTTY */ + + er = TerminalSet(tty, &ioval); + + if (er == -1) { + printf("Error occured while set %x: errno=%d\n", mode, errno); + reset_exit(SIGNAL_ARGLIST); + } +} + +void +ttymode_reset(int mode, int imode) +{ + int er; + TerminalMode ioval; + + TerminalGet(tty, &ioval); + MODEFLAG(ioval) &= ~mode; +#ifndef SGTTY + IMODEFLAG(ioval) &= ~imode; +#endif /* not SGTTY */ + + er = TerminalSet(tty, &ioval); + + if (er == -1) { + printf("Error occured while reset %x: errno=%d\n", mode, errno); + reset_exit(SIGNAL_ARGLIST); + } +} + +#ifndef SGTTY +void +set_cc(int spec, int val) +{ + int er; + TerminalMode ioval; + + TerminalGet(tty, &ioval); + ioval.c_cc[spec] = val; + er = TerminalSet(tty, &ioval); + if (er == -1) { + printf("Error occured: errno=%d\n", errno); + reset_exit(SIGNAL_ARGLIST); + } +} +#endif /* not SGTTY */ + +void +close_tty(void) +{ + close(tty); +} + +void +reset_tty(void) +{ + if (DisplayCode != CODE_EUC && DisplayCode != CODE_SJIS) +#if defined(__EMX__)&&!defined(JP_CHARSET) + if(!CodePage) +#endif + writestr("\033(B"); /* designate US_ASCII */ + writestr(T_op); /* turn off */ + writestr(T_me); + if (!Do_not_use_ti_te) { + if (T_te && *T_te) + writestr(T_te); + else + writestr(T_cl); + } + writestr(T_se); /* reset terminal */ + fflush(ttyf); + TerminalSet(tty, &d_ioval); + close(tty); +} + +MySignalHandler +reset_exit(SIGNAL_ARG) +{ + reset_tty(); +#ifdef MOUSE + if (mouseActive) + mouse_end(); +#endif /* MOUSE */ + exit(0); + SIGNAL_RETURN; +} + +MySignalHandler +error_dump(SIGNAL_ARG) +{ + signal(SIGIOT, SIG_DFL); + reset_tty(); + abort(); + SIGNAL_RETURN; +} + +void +set_int(void) +{ + signal(SIGHUP, reset_exit); + signal(SIGINT, reset_exit); + signal(SIGQUIT, reset_exit); + signal(SIGTERM, reset_exit); + signal(SIGILL, error_dump); + signal(SIGIOT, error_dump); + signal(SIGFPE, error_dump); +#ifdef SIGBUS + signal(SIGBUS, error_dump); +#endif /* SIGBUS */ +/* signal(SIGSEGV, error_dump); */ +} + + +static void +setgraphchar(void) +{ + int c, i, n; + + for (c = 0; c < 96; c++) + gcmap[c] = (char) (c + ' '); + + if (!T_ac) + return; + + n = strlen(T_ac); + for (i = 0; i < n - 1; i += 2) { + c = (unsigned) T_ac[i] - ' '; + if (c >= 0 && c < 96) + gcmap[c] = T_ac[i + 1]; + } +} + +#define graphchar(c) (((unsigned)(c)>=' ' && (unsigned)(c)<128)? gcmap[(c)-' '] : (c)) + +#define GETSTR(v,s) {v = pt; suc = tgetstr(s,&pt); if (!suc) v = ""; else v = allocStr(suc,0); } + +void +getTCstr(void) +{ + char *ent; + char *suc; + char *pt = funcstr; + int r; + +#ifdef __DJGPP__ + ent = getenv("TERM") ? getenv("TERM") : "dosansi"; +#else + ent = getenv("TERM"); +#endif /* __DJGPP__ */ + r = tgetent(bp, ent); + if (r != 1) { + /* Can't find termcap entry */ + fprintf(stderr, "Can't find termcap entry %s\n", ent); + reset_exit(SIGNAL_ARGLIST); + } + + GETSTR(T_ce, "ce"); /* clear to the end of line */ + GETSTR(T_cd, "cd"); /* clear to the end of display */ + GETSTR(T_kr, "nd"); /* cursor right */ + if (suc == NULL) + GETSTR(T_kr, "kr"); + if (tgetflag("bs")) + T_kl = "\b"; /* cursor left */ + else { + GETSTR(T_kl, "le"); + if (suc == NULL) + GETSTR(T_kl, "kb"); + if (suc == NULL) + GETSTR(T_kl, "kl"); + } + GETSTR(T_cr, "cr"); /* carrige return */ + GETSTR(T_ta, "ta"); /* tab */ + GETSTR(T_sc, "sc"); /* save cursor */ + GETSTR(T_rc, "rc"); /* restore cursor */ + GETSTR(T_so, "so"); /* standout mode */ + GETSTR(T_se, "se"); /* standout mode end */ + GETSTR(T_us, "us"); /* underline mode */ + GETSTR(T_ue, "ue"); /* underline mode end */ + GETSTR(T_md, "md"); /* bold mode */ + GETSTR(T_me, "me"); /* bold mode end */ + GETSTR(T_cl, "cl"); /* clear screen */ + GETSTR(T_cm, "cm"); /* cursor move */ + GETSTR(T_al, "al"); /* append line */ + GETSTR(T_sr, "sr"); /* scroll reverse */ + GETSTR(T_ti, "ti"); /* terminal init */ + GETSTR(T_te, "te"); /* terminal end */ + GETSTR(T_nd, "nd"); /* move right one space */ + GETSTR(T_eA, "eA"); /* enable alternative charset */ + GETSTR(T_as, "as"); /* alternative (graphic) charset start */ + GETSTR(T_ae, "ae"); /* alternative (graphic) charset end */ + GETSTR(T_ac, "ac"); /* graphics charset pairs */ + GETSTR(T_op, "op"); /* set default color pair to its original + * * * * * * * value */ +#ifdef CYGWIN +/* for TERM=pcansi on MS-DOS prompt. * T_as = "\033[12m"; * T_ae = + * "\033[10m"; * T_ac = + * "l\001k\002m\003j\004x\005q\006n\020a\024v\025w\026u\027t\031"; */ + T_as[0] = '\0'; + T_ae[0] = '\0'; + T_ac[0] = '\0'; +#endif /* CYGWIN */ + + LINES = COLS = 0; + setlinescols(); + setgraphchar(); +} + +#ifndef TIOCGWINSZ +#include <sys/ioctl.h> +#endif /* not TIOCGWINSZ */ + +void +setlinescols(void) +{ + char *p; + int i; +#ifdef __EMX__ + { + int s[2]; + _scrsize(s); + COLS = s[0]; + LINES = s[1]; + + if (getenv("WINDOWID")) { + FILE *fd = popen("scrsize", "rt"); + if (fd) { + fscanf(fd, "%i %i", &COLS, &LINES); + pclose(fd); + } + } +#ifndef JP_CHARSET + else{ + ULONG CpList[8],CpSize; + CodePage=-1; + if(!DosQueryCp(sizeof(CpList),CpList,&CpSize)) + CodePage=*CpList; + } +#endif + } +#elif defined(TERMIOS) && defined(TIOCGWINSZ) + struct winsize wins; + + i = ioctl(tty, TIOCGWINSZ, &wins); + if (i >= 0 && wins.ws_row != 0 && wins.ws_col != 0) { + LINES = wins.ws_row; + COLS = wins.ws_col; + } +#endif /* defined(TERMIOS) && defined(TIOCGWINSZ) + * + */ + if (LINES <= 0 && + (p = getenv("LINES")) != NULL && + (i = atoi(p)) >= 0) + LINES = i; + if (COLS <= 0 && + (p = getenv("COLUMNS")) != NULL && + (i = atoi(p)) >= 0) + COLS = i; + if (LINES <= 0) + LINES = tgetnum("li"); /* number of line */ + if (COLS <= 0) + COLS = tgetnum("co"); /* number of column */ + if (COLS > MAX_COLUMN) + COLS = MAX_COLUMN; + if (LINES > MAX_LINE) + LINES = MAX_LINE; +} + +void +setupscreen(void) +{ + int i; + + if (LINES + 1 > max_LINES) { + max_LINES = LINES + 1; + max_COLS = 0; + ScreenElem = New_N(Screen, max_LINES); + ScreenImage = New_N(Screen *, max_LINES); + } + if (COLS + 1 > max_COLS) { + max_COLS = COLS + 1; + for (i = 0; i < max_LINES; i++) { + ScreenElem[i].lineimage = New_N(char, max_COLS); + ScreenElem[i].lineprop = New_N(l_prop, max_COLS); + } + } + for (i = 0; i < LINES; i++) { + ScreenImage[i] = &ScreenElem[i]; + ScreenImage[i]->lineprop[0] = S_EOL; + ScreenImage[i]->isdirty = 0; + } + for (; i < max_LINES; i++) { + ScreenElem[i].isdirty = L_UNUSED; + } + + clear(); +} + +/* + * Screen initialize + */ +int +initscr(void) +{ + if (set_tty() < 0) + return -1; + set_int(); + getTCstr(); + if (T_ti && !Do_not_use_ti_te) + writestr(T_ti); + setupscreen(); + return 0; +} + +#ifdef JP_CHARSET +static int wmode = C_ASCII; +static char wbuf; +#endif + +int +write1(char c) +{ +#ifdef SCREEN_DEBUG + usleep(50); +#endif /* SCREEN_DEBUG */ +#ifdef JP_CHARSET + if (IS_KANJI(c)) { + switch (wmode) { + case C_ASCII: + switch_wchar(ttyf); + case C_WCHAR2: + wmode = C_WCHAR1; + wbuf = c; + break; + case C_WCHAR1: + wmode = C_WCHAR2; + putchars((unsigned char) wbuf, (unsigned char) c, ttyf); + break; + } + } + else { + switch (wmode) { + case C_ASCII: + break; + case C_WCHAR1: + /* ignore byte */ + wmode = C_ASCII; + switch_ascii(ttyf); + break; + case C_WCHAR2: + wmode = C_ASCII; + switch_ascii(ttyf); + break; + } + putc(c, ttyf); + } +#else /* not JP_CHARSET */ + putc(c, ttyf); +#endif /* not JP_CHARSET */ +#ifdef SCREEN_DEBUG + fflush(ttyf); +#endif /* SCREEN_DEBUG */ + return 0; +} + +#ifdef JP_CHARSET +void +endline(void) +{ /* End of line */ + if (wmode != C_ASCII) { + switch_ascii(ttyf); + wmode = C_ASCII; + } +} + +void +switch_ascii(FILE * f) +{ + extern char *GetSOCode(char); + if (CODE_JIS(DisplayCode)) { + fputs(GetSOCode(DisplayCode), f); + } +} + +void +switch_wchar(FILE * f) +{ + extern char *GetSICode(char); + if (CODE_JIS(DisplayCode)) { + fputs(GetSICode(DisplayCode), f); + } +} + +void +putchars(unsigned char c1, unsigned char c2, FILE * f) +{ + Str s; + char *p; + + switch (DisplayCode) { + case CODE_EUC: + putc(c1, f); + putc(c2, f); + return; + case CODE_JIS_n: + case CODE_JIS_m: + case CODE_JIS_N: + case CODE_JIS_j: + case CODE_JIS_J: + putc(c1 & 0x7f, f); + putc(c2 & 0x7f, f); + return; + case CODE_SJIS: + s = Strnew_size(3); + put_sjis(s, c1 & 0x7f, c2 & 0x7f); + break; + } + if (! s->length) { + putc('?', f); + putc('?', f); + return; + } + for (p = s->ptr; *p != '\0'; p++) + putc(*p, f); +} +#endif /* JP_CHARSET */ + +void +move(int line, int column) +{ + if (line >= 0 && line < LINES) + CurLine = line; + if (column >= 0 && column < COLS) + CurColumn = column; +} + +#ifdef BG_COLOR +#define M_SPACE (S_SCREENPROP|S_COLORED|S_BCOLORED|S_GRAPHICS) +#else /* not BG_COLOR */ +#define M_SPACE (S_SCREENPROP|S_COLORED|S_GRAPHICS) +#endif /* not BG_COLOR */ + +static int +need_redraw(char c1, l_prop pr1, char c2, l_prop pr2) +{ + if (c1 != c2) + return 1; + if (c1 == ' ') + return (pr1 ^ pr2) & M_SPACE & ~S_DIRTY; + + if ((pr1 ^ pr2) & ~S_DIRTY) + return 1; + + return 0; +} + +#define M_CEOL (~(M_SPACE|C_WHICHCHAR)) + +void +addch(char c) +{ + char *p; + l_prop *pr; + int dest, i; + short *dirty; +#ifdef __EMX__ + extern int CodePage; +#endif + + if (CurColumn == COLS) + wrap(); + if (CurColumn >= COLS) + return; + p = ScreenImage[CurLine]->lineimage; + pr = ScreenImage[CurLine]->lineprop; + dirty = &ScreenImage[CurLine]->isdirty; + + /* Eliminate unprintables according to * iso-8859-*. + * Particularly 0x96 messes up T.Dickey's * (xfree-)xterm */ + if (IS_INTERNAL(c)) + c = ' '; + + if (pr[CurColumn] & S_EOL) { + if (c == ' ' && +#ifdef JP_CHARSET + CHMODE(CurrentMode) != C_WCHAR1 && +#endif /* JP_CHARSET */ + !(CurrentMode & M_SPACE)) { + CurColumn++; + return; + } + for (i = CurColumn; i >= 0 && (pr[i] & S_EOL); i--) { + p[i] = ' '; + SETPROP(pr[i], (pr[i] & M_CEOL) | C_ASCII); + } + } + + if (c == '\t' || c == '\n' || c == '\r' || c == '\b') + SETCHMODE(CurrentMode, C_CTRL); +#ifdef JP_CHARSET + else if (CHMODE(CurrentMode) == C_WCHAR1) + SETCHMODE(CurrentMode, C_WCHAR2); + else if (IS_KANJI1(c)) + SETCHMODE(CurrentMode, C_WCHAR1); +#endif /* JP_CHARSET */ + else if (!IS_CNTRL(c)) + SETCHMODE(CurrentMode, C_ASCII); + else + return; + + /* Required to erase bold or underlined character for some * terminal + * emulators. */ + if (((pr[CurColumn] & S_BOLD) && + need_redraw(p[CurColumn], pr[CurColumn], c, CurrentMode)) || + ((pr[CurColumn] & S_UNDERLINE) && !(CurrentMode & S_UNDERLINE))) { + touch_line(); + if (CurColumn < COLS - 1) { + touch_column(CurColumn + 1); + if (pr[CurColumn + 1] & S_EOL) { + p[CurColumn + 1] = ' '; + SETPROP(pr[CurColumn + 1], (pr[CurColumn + 1] & M_CEOL) | C_ASCII); + } +#ifdef JP_CHARSET + else if (CHMODE(pr[CurColumn + 1]) == C_WCHAR1 && CurColumn < COLS - 2) + touch_column(CurColumn + 2); +#endif /* JP_CHARSET */ + } + } + +#ifdef JP_CHARSET + if (CurColumn >= 1 && CHMODE(pr[CurColumn - 1]) == C_WCHAR1 && + CHMODE(CurrentMode) != C_WCHAR2) { + p[CurColumn - 1] = ' '; + SETPROP(pr[CurColumn - 1], (pr[CurColumn - 1] & ~C_WHICHCHAR) | C_ASCII); + touch_line(); + touch_column(CurColumn - 1); + } + + if (CurColumn < COLS - 1 && CHMODE(pr[CurColumn + 1]) == C_WCHAR2 && + CHMODE(CurrentMode) != C_WCHAR1) { + p[CurColumn + 1] = ' '; + SETPROP(pr[CurColumn + 1], (pr[CurColumn + 1] & ~C_WHICHCHAR) | C_ASCII); + touch_line(); + touch_column(CurColumn + 1); + } + + if (CurColumn == COLS - 1 && CHMODE(CurrentMode) == C_WCHAR1) { + wrap(); + p = ScreenImage[CurLine]->lineimage; + pr = ScreenImage[CurLine]->lineprop; + } +#endif /* JP_CHARSET */ + if (CHMODE(CurrentMode) != C_CTRL) { + if (need_redraw(p[CurColumn], pr[CurColumn], c, CurrentMode)) { + p[CurColumn] = c; + SETPROP(pr[CurColumn], CurrentMode); + touch_line(); + touch_column(CurColumn); +#ifdef JP_CHARSET + if (CHMODE(CurrentMode) == C_WCHAR1) + touch_column(CurColumn + 1); + else if (CHMODE(CurrentMode) == C_WCHAR2) + touch_column(CurColumn - 1); +#endif /* JP_CHARSET */ + } + CurColumn++; + } + else if (c == '\t') { + dest = (CurColumn + tab_step) / tab_step * tab_step; + if (dest >= COLS) { + wrap(); + touch_line(); + dest = tab_step; + p = ScreenImage[CurLine]->lineimage; + pr = ScreenImage[CurLine]->lineprop; + } + for (i = CurColumn; i < dest; i++) { + if (need_redraw(p[i], pr[i], ' ', CurrentMode)) { + p[i] = ' '; + SETPROP(pr[i], CurrentMode); + touch_line(); + touch_column(i); + } + } + CurColumn = i; + } + else if (c == '\n') { + wrap(); + } + else if (c == '\r') { /* Carrige return */ + CurColumn = 0; + } + else if (c == '\b' && CurColumn > 0) { /* Backspace */ + CurColumn--; +#ifdef JP_CHARSET + if (CurColumn > 0 && + CHMODE(pr[CurColumn]) == C_WCHAR2) + CurColumn--; +#endif /* JP_CHARSET */ + } +} + +void +wrap(void) +{ + if (CurLine == LINES - 1) + return; + CurLine++; + CurColumn = 0; +} + +void +touch_column(int col) +{ + if (col >= 0 && col < COLS) + ScreenImage[CurLine]->lineprop[col] |= S_DIRTY; +} + +void +touch_line(void) +{ + if (!(ScreenImage[CurLine]->isdirty & L_DIRTY)) { + int i; + for (i = 0; i < COLS; i++) + ScreenImage[CurLine]->lineprop[i] &= ~S_DIRTY; + ScreenImage[CurLine]->isdirty |= L_DIRTY; + } + +} + +void +standout(void) +{ + CurrentMode |= S_STANDOUT; +} + +void +standend(void) +{ + CurrentMode &= ~S_STANDOUT; +} + +void +toggle_stand(void) +{ + l_prop *pr = ScreenImage[CurLine]->lineprop; + pr[CurColumn] ^= S_STANDOUT; +#ifdef JP_CHARSET + if (CHMODE(pr[CurColumn]) == C_WCHAR1) + pr[CurColumn + 1] ^= S_STANDOUT; +#endif /* JP_CHARSET */ +} + +void +bold(void) +{ + CurrentMode |= S_BOLD; +} + +void +boldend(void) +{ + CurrentMode &= ~S_BOLD; +} + +void +underline(void) +{ + CurrentMode |= S_UNDERLINE; +} + +void +underlineend(void) +{ + CurrentMode &= ~S_UNDERLINE; +} + +void +graphstart(void) +{ + CurrentMode |= S_GRAPHICS; +} + +void +graphend(void) +{ + CurrentMode &= ~S_GRAPHICS; +} + +int +graph_ok(void) +{ +#ifndef KANJI_SYMBOLS + if (no_graphic_char) + return 0; +#endif /* not KANJI_SYMBOLS */ + return T_as[0] != 0 && T_ae[0] != 0 && T_ac[0] != 0; +} + +void +setfcolor(int color) +{ + CurrentMode &= ~COL_FCOLOR; + if ((color & 0xf) <= 7) + CurrentMode |= (((color & 7) | 8) << 8); +} + +static char * +color_seq(int colmode) +{ + static char seqbuf[32]; + sprintf(seqbuf, "\033[%dm", ((colmode >> 8) & 7) + 30); + return seqbuf; +} + +#ifdef BG_COLOR +void +setbcolor(int color) +{ + CurrentMode &= ~COL_BCOLOR; + if ((color & 0xf) <= 7) + CurrentMode |= (((color & 7) | 8) << 12); +} + +static char * +bcolor_seq(int colmode) +{ + static char seqbuf[32]; + sprintf(seqbuf, "\033[%dm", ((colmode >> 12) & 7) + 40); + return seqbuf; +} +#endif /* BG_COLOR */ + +#define RF_NEED_TO_MOVE 0 +#define RF_CR_OK 1 +#define RF_NONEED_TO_MOVE 2 +#ifdef BG_COLOR +#define M_MEND (S_STANDOUT|S_UNDERLINE|S_BOLD|S_COLORED|S_BCOLORED|S_GRAPHICS) +#else /* not BG_COLOR */ +#define M_MEND (S_STANDOUT|S_UNDERLINE|S_BOLD|S_COLORED|S_GRAPHICS) +#endif /* not BG_COLOR */ +void +refresh(void) +{ + int line, col, pcol; + int pline = CurLine; + int moved = RF_NEED_TO_MOVE; + char *pc; + l_prop *pr, mode = 0; + l_prop color = COL_FTERM; +#ifdef BG_COLOR + l_prop bcolor = COL_BTERM; +#endif /* BG_COLOR */ + short *dirty; + + for (line = 0; line < LINES; line++) { + dirty = &ScreenImage[line]->isdirty; + if (*dirty & L_DIRTY) { + *dirty &= ~L_DIRTY; + pc = ScreenImage[line]->lineimage; + pr = ScreenImage[line]->lineprop; + for (col = 0; col < COLS && !(pr[col] & S_EOL); col++) { + if (*dirty & L_NEED_CE && col >= ScreenImage[line]->eol) { + if (need_redraw(pc[col], pr[col], ' ', 0)) + break; + } + else { + if (pr[col] & S_DIRTY) + break; + } + } + if (*dirty & (L_NEED_CE | L_CLRTOEOL)) { + pcol = ScreenImage[line]->eol; + if (pcol >= COLS) { + *dirty &= ~(L_NEED_CE | L_CLRTOEOL); + pcol = col; + } + } + else { + pcol = col; + } + if (line < LINES - 2 && pline == line - 1 && pcol == 0) { + switch (moved) { + case RF_NEED_TO_MOVE: + MOVE(line, 0); + moved = RF_CR_OK; + break; + case RF_CR_OK: + write1('\n'); + write1('\r'); + break; + case RF_NONEED_TO_MOVE: + moved = RF_CR_OK; + break; + } + } + else { + MOVE(line, pcol); + moved = RF_CR_OK; + } + if (*dirty & (L_NEED_CE | L_CLRTOEOL)) { + writestr(T_ce); + if (col != pcol) + MOVE(line, col); + } + pline = line; + pcol = col; + for (; col < COLS; col++) { + if (pr[col] & S_EOL) + break; + + /* + * some terminal emulators do linefeed when a + * character is put on COLS-th column. this behavior + * is different from one of vt100, but such terminal + * emulators are used as vt100-compatible + * emulators. This behaviour causes scroll when a + * character is drawn on (COLS-1,LINES-1) point. To + * avoid the scroll, I prohibit to draw character on + * (COLS-1,LINES-1). + */ +#ifndef BG_COLOR + if (line == LINES - 1 && col == COLS - 1) + break; +#endif /* not BG_COLOR */ + if ((!(pr[col] & S_STANDOUT) && (mode & S_STANDOUT)) || + (!(pr[col] & S_UNDERLINE) && (mode & S_UNDERLINE)) || + (!(pr[col] & S_BOLD) && (mode & S_BOLD)) || + (!(pr[col] & S_COLORED) && (mode & S_COLORED)) +#ifdef BG_COLOR + || (!(pr[col] & S_BCOLORED) && (mode & S_BCOLORED)) +#endif /* BG_COLOR */ + || (!(pr[col] & S_GRAPHICS) && (mode & S_GRAPHICS))) { + if ((!(pr[col] & S_COLORED) && (mode & S_COLORED)) +#ifdef BG_COLOR + || (!(pr[col] & S_BCOLORED) && (mode & S_BCOLORED)) +#endif /* BG_COLOR */ + ) + writestr(T_op); + if (!(pr[col] & S_GRAPHICS) && (mode & S_GRAPHICS)) + writestr(T_ae); + writestr(T_me); + mode &= ~M_MEND; + } + if ((*dirty & L_NEED_CE && col >= ScreenImage[line]->eol) ? + need_redraw(pc[col], pr[col], ' ', 0) : (pr[col] & S_DIRTY)) { + if (pcol == col - 1) + writestr(T_nd); + else if (pcol != col) + MOVE(line, col); + + if ((pr[col] & S_COLORED) && (pr[col] ^ mode) & COL_FCOLOR) { + color = (pr[col] & COL_FCOLOR); + mode = ((mode & ~COL_FCOLOR) | color); + writestr(color_seq(color)); + } +#ifdef BG_COLOR + if ((pr[col] & S_BCOLORED) && (pr[col] ^ mode) & COL_BCOLOR) { + bcolor = (pr[col] & COL_BCOLOR); + mode = ((mode & ~COL_BCOLOR) | bcolor); + writestr(bcolor_seq(bcolor)); + } +#endif /* BG_COLOR */ + if ((pr[col] & S_STANDOUT) && !(mode & S_STANDOUT)) { + writestr(T_so); + mode |= S_STANDOUT; + } + if ((pr[col] & S_UNDERLINE) && !(mode & S_UNDERLINE)) { + writestr(T_us); + mode |= S_UNDERLINE; + } + if ((pr[col] & S_BOLD) && !(mode & S_BOLD)) { + writestr(T_md); + mode |= S_BOLD; + } + if ((pr[col] & S_GRAPHICS) && !(mode & S_GRAPHICS)) { + if (!graph_enabled) { + graph_enabled = 1; + writestr(T_eA); + } + writestr(T_as); + mode |= S_GRAPHICS; + } + write1((pr[col] & S_GRAPHICS) ? graphchar(pc[col]) : pc[col]); + pcol = col + 1; + } + } + if (col == COLS) + moved = RF_NEED_TO_MOVE; + for (; col < COLS && !(pr[col] & S_EOL); col++) + pr[col] |= S_EOL; + } + *dirty &= ~(L_NEED_CE | L_CLRTOEOL); + if (mode & M_MEND) { + if (mode & (S_COLORED +#ifdef BG_COLOR + | S_BCOLORED +#endif /* BG_COLOR */ + )) + writestr(T_op); + if (mode & S_GRAPHICS) + writestr(T_ae); + writestr(T_me); + mode &= ~M_MEND; + } +#ifdef JP_CHARSET + endline(); +#endif /* JP_CHARSET */ + } + MOVE(CurLine, CurColumn); + fflush(ttyf); +} + +void +clear(void) +{ + int i, j; + l_prop *p; + writestr(T_cl); + move(0, 0); + for (i = 0; i < LINES; i++) { + ScreenImage[i]->isdirty = 0; + p = ScreenImage[i]->lineprop; + for (j = 0; j < COLS; j++) { + p[j] = S_EOL; + } + } + CurrentMode = C_ASCII; +} + +static void +scroll_raw(void) +{ /* raw scroll */ + MOVE(LINES - 1, 0); + write1('\n'); +} + +void +scroll(int n) +{ /* scroll up */ + int cli = CurLine, cco = CurColumn; + Screen *t; + int i, j, k; + + i = LINES; + j = n; + do { + k = j; + j = i % k; + i = k; + } while (j); + do { + k--; + i = k; + j = (i + n) % LINES; + t = ScreenImage[k]; + while (j != k) { + ScreenImage[i] = ScreenImage[j]; + i = j; + j = (i + n) % LINES; + } + ScreenImage[i] = t; + } while (k); + + for (i = 0; i < n; i++) { + t = ScreenImage[LINES - 1 - i]; + t->isdirty = 0; + for (j = 0; j < COLS; j++) + t->lineprop[j] = S_EOL; + scroll_raw(); + } + move(cli, cco); +} + +void +rscroll(int n) +{ /* scroll down */ + int cli = CurLine, cco = CurColumn; + Screen *t; + int i, j, k; + + i = LINES; + j = n; + do { + k = j; + j = i % k; + i = k; + } while (j); + do { + k--; + i = k; + j = (LINES + i - n) % LINES; + t = ScreenImage[k]; + while (j != k) { + ScreenImage[i] = ScreenImage[j]; + i = j; + j = (LINES + i - n) % LINES; + } + ScreenImage[i] = t; + } while (k); + if (T_sr && *T_sr) { + MOVE(0, 0); + for (i = 0; i < n; i++) { + t = ScreenImage[i]; + t->isdirty = 0; + for (j = 0; j < COLS; j++) + t->lineprop[j] = S_EOL; + writestr(T_sr); + } + move(cli, cco); + } + else { + for (i = 0; i < LINES; i++) { + t = ScreenImage[i]; + t->isdirty |= L_DIRTY | L_NEED_CE; + for (j = 0; j < COLS; j++) { + t->lineprop[j] |= S_DIRTY; + } + } + } +} + +#if 0 +void +need_clrtoeol(void) +{ + /* Clear to the end of line as the need arises */ + l_prop *lprop = ScreenImage[CurLine]->lineprop; + + if (lprop[CurColumn] & S_EOL) + return; + + if (!(ScreenImage[CurLine]->isdirty & (L_NEED_CE | L_CLRTOEOL)) || + ScreenImage[CurLine]->eol > CurColumn) + ScreenImage[CurLine]->eol = CurColumn; + + ScreenImage[CurLine]->isdirty |= L_NEED_CE; +} +#endif /* 0 */ + +void +clrtoeol(void) +{ /* Clear to the end of line */ + int i; + l_prop *lprop = ScreenImage[CurLine]->lineprop; + + if (lprop[CurColumn] & S_EOL) + return; + + if (!(ScreenImage[CurLine]->isdirty & (L_NEED_CE | L_CLRTOEOL)) || + ScreenImage[CurLine]->eol > CurColumn) + ScreenImage[CurLine]->eol = CurColumn; + + ScreenImage[CurLine]->isdirty |= L_CLRTOEOL; + touch_line(); + for (i = CurColumn; i < COLS && !(lprop[i] & S_EOL); i++) { + lprop[i] = S_EOL | S_DIRTY; + } +} + +#ifdef BG_COLOR +void +clrtoeol_with_bcolor(void) +{ + int i, cli, cco; + l_prop pr; + + if (!(CurrentMode & S_BCOLORED)) { + clrtoeol(); + return; + } + cli = CurLine; + cco = CurColumn; + pr = CurrentMode; + CurrentMode = (CurrentMode & (M_CEOL | S_BCOLORED)) | C_ASCII; + for (i = CurColumn; i < COLS; i++) + addch(' '); + move(cli, cco); + CurrentMode = pr; +} + +void +clrtoeolx(void) +{ + clrtoeol_with_bcolor(); +} +#else /* not BG_COLOR */ + +void +clrtoeolx(void) +{ + clrtoeol(); +} +#endif /* not BG_COLOR */ + +void +clrtobot_eol(void (*clrtoeol) ()) +{ + int l, c; + + l = CurLine; + c = CurColumn; + (*clrtoeol) (); + CurColumn = 0; + CurLine++; + for (; CurLine < LINES; CurLine++) + (*clrtoeol) (); + CurLine = l; + CurColumn = c; +} + +void +clrtobot(void) +{ + clrtobot_eol(clrtoeol); +} + +void +clrtobotx(void) +{ + clrtobot_eol(clrtoeolx); +} + +#if 0 +void +no_clrtoeol(void) +{ + int i; + l_prop *lprop = ScreenImage[CurLine]->lineprop; + + ScreenImage[CurLine]->isdirty &= ~L_CLRTOEOL; +} +#endif /* 0 */ + +void +addstr(char *s) +{ + while (*s != '\0') + addch(*(s++)); +} + +void +addnstr(char *s, int n) +{ + int i; + +#ifdef JP_CHARSET + for (i = 0; i < n - 1 && *s != '\0'; i++) + addch(*(s++)); + if (*s != '\0') { + if (IS_KANJI2(*s)) { + /* WCHAR */ + if (CHMODE(CurrentMode) == C_WCHAR1) { + /* WCHAR second byte */ + addch(*s); + } + } + else { + /* Ascii or WCHAR2 */ + addch(*s); + } + } +#else /* not JP_CHARSET */ + for (i = 0; i < n && *s != '\0'; i++) + addch(*(s++)); +#endif /* not JP_CHARSET */ +} + +void +addnstr_sup(char *s, int n) +{ + int i; + +#ifdef JP_CHARSET + for (i = 0; i < n - 1 && *s != '\0'; i++) + addch(*(s++)); + if (*s != '\0') { + if (IS_KANJI2(*s)) { + /* WCHAR */ + if (CHMODE(CurrentMode) == C_WCHAR1) { + /* WCHAR second byte */ + addch(*s); + i++; + } + } + else { + /* Ascii or WCHAR2 */ + addch(*s); + i++; + } + } +#else /* not JP_CHARSET */ + for (i = 0; i < n && *s != '\0'; i++) + addch(*(s++)); +#endif /* not JP_CHARSET */ + for (; i < n; i++) + addch(' '); +} + +void +crmode(void) +#ifndef SGTTY +{ + ttymode_reset(ICANON, IXON); + ttymode_set(ISIG, 0); +#ifdef TERMIOS + set_cc(VMIN, 1); +#else /* not TERMIOS */ + set_cc(VEOF, 1); +#endif /* not TERMIOS */ +} +#else /* SGTTY */ +{ + ttymode_set(CBREAK, 0); +} +#endif /* SGTTY */ + +void +nocrmode(void) +#ifndef SGTTY +{ + ttymode_set(ICANON, 0); +#ifdef TERMIOS + set_cc(VMIN, 4); +#else /* not TERMIOS */ + set_cc(VEOF, 4); +#endif /* not TERMIOS */ +} +#else /* SGTTY */ +{ + ttymode_reset(CBREAK, 0); +} +#endif /* SGTTY */ + +void +term_echo(void) +{ + ttymode_set(ECHO, 0); +} + +void +term_noecho(void) +{ + ttymode_reset(ECHO, 0); +} + +void +term_raw(void) +#ifndef SGTTY +#ifdef IEXTEN +#define TTY_MODE ISIG|ICANON|ECHO|IEXTEN +#else /* not IEXTEN */ +#define TTY_MODE ISIG|ICANON|ECHO +#endif /* not IEXTEN */ +{ + ttymode_reset(TTY_MODE, IXON | IXOFF); +#ifdef TERMIOS + set_cc(VMIN, 1); +#else /* not TERMIOS */ + set_cc(VEOF, 1); +#endif /* not TERMIOS */ +} +#else /* SGTTY */ +{ + ttymode_set(RAW, 0); +} +#endif /* SGTTY */ + +void +term_cooked(void) +#ifndef SGTTY +{ +#ifdef __EMX__ + /* On XFree86/OS2, some scrambled characters + * will appear when asserting IEXTEN flag. + */ + ttymode_set((TTY_MODE)&~IEXTEN,0); +#else + ttymode_set(TTY_MODE, 0); +#endif +#ifdef TERMIOS + set_cc(VMIN, 4); +#else /* not TERMIOS */ + set_cc(VEOF, 4); +#endif /* not TERMIOS */ +} +#else /* SGTTY */ +{ + ttymode_reset(RAW, 0); +} +#endif /* SGTTY */ + +void +term_cbreak(void) +{ + term_cooked(); + term_noecho(); +} + +char +getch(void) +{ + char c; + + read(tty, &c, 1); + return c; +} + +#ifdef MOUSE +#ifdef USE_GPM +char +wgetch(void) +{ + char c; + + read(tty, &c, 1); + return c; +} + +int +do_getch() +{ + if (is_xterm) + return getch(); + else + return Gpm_Getch(); +} +#endif /* USE_GPM */ + +#ifdef USE_SYSMOUSE +int +sysm_getch() +{ + fd_set rfd; + int key, x, y; + + FD_ZERO(&rfd); + FD_SET(tty, &rfd); + while (select(tty + 1, &rfd, NULL, NULL, NULL) <= 0) { + if (errno == EINTR) { + x = xpix / cwidth; + y = ypix / cheight; + key = (*sysm_handler) (x, y, nbs, obs); + if (key != 0) + return key; + } + } + return getch(); +} + +int +do_getch() +{ + if (is_xterm) + return getch(); + else + return sysm_getch(); +} + +MySignalHandler +sysmouse(SIGNAL_ARG) +{ + struct mouse_info mi; + + mi.operation = MOUSE_GETINFO; + if (ioctl(tty, CONS_MOUSECTL, &mi) == -1) + return; + xpix = mi.u.data.x; + ypix = mi.u.data.y; + obs = nbs; + nbs = mi.u.data.buttons & 0x7; + /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */ + mi.operation = MOUSE_HIDE; + ioctl(tty, CONS_MOUSECTL, &mi); + mi.operation = MOUSE_SHOW; + ioctl(tty, CONS_MOUSECTL, &mi); +} +#endif /* USE_SYSMOUSE */ +#endif /* MOUSE */ + +void +bell(void) +{ + write1(7); +} + +void +skip_escseq(void) +{ + int c; + + c = getch(); + if (c == '[' || c == 'O') { + c = getch(); + while (IS_DIGIT(c)) + c = getch(); + } +} + +void +sleep_till_anykey(int sec, int purge) +{ + fd_set rfd; + struct timeval tim; + int er, c; + TerminalMode ioval; + + TerminalGet(tty, &ioval); + term_raw(); + + tim.tv_sec = sec; + tim.tv_usec = 0; + + FD_ZERO(&rfd); + FD_SET(tty, &rfd); + + if (select(tty + 1, &rfd, 0, 0, &tim) > 0 && purge) { + c = getch(); + if (c == ESC_CODE) + skip_escseq(); + } + er = TerminalSet(tty, &ioval); + if (er == -1) { + printf("Error occured: errno=%d\n", errno); + reset_exit(SIGNAL_ARGLIST); + } +} + +#ifdef MOUSE + +#define XTERM_ON {fputs("\033[?1001s\033[?1000h",ttyf); flush_tty();} +#define XTERM_OFF {fputs("\033[?1000l\033[?1001r",ttyf); flush_tty();} + +#ifdef USE_GPM +/* Linux console with GPM support */ + +void +mouse_init() +{ + Gpm_Connect conn; + extern int gpm_process_mouse(Gpm_Event *, void *); + + if (mouseActive) + return; + if (is_xterm) { + XTERM_ON; + } + else { + conn.eventMask = ~0; + conn.defaultMask = 0; + conn.maxMod = 0; + conn.minMod = 0; + Gpm_Open(&conn, 0); /* don't care even if it fails */ + gpm_handler = gpm_process_mouse; + } + mouseActive = 1; +} + +void +mouse_end() +{ + if (mouseActive == 0) + return; + if (is_xterm) { + XTERM_OFF; + } + else + Gpm_Close(); + mouseActive = 0; +} + +#elif defined(USE_SYSMOUSE) +/* *BSD console with sysmouse support */ +void +mouse_init() +{ + mouse_info_t mi; + extern int sysm_process_mouse(); + + if (mouseActive) + return; + if (is_xterm) { + XTERM_ON; + } + else { +#if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO) /* FreeBSD > 2.x */ +#ifndef FBIO_GETMODE /* FreeBSD 3.x */ +#define FBIO_GETMODE CONS_GET +#define FBIO_MODEINFO CONS_MODEINFO +#endif /* FBIO_GETMODE */ + video_info_t vi; + + if (ioctl(tty, FBIO_GETMODE, &vi.vi_mode) != -1 && + ioctl(tty, FBIO_MODEINFO, &vi) != -1) { + cwidth = vi.vi_cwidth; + cheight = vi.vi_cheight; + } +#endif /* defined(FBIO_MODEINFO) || + * defined(CONS_MODEINFO) */ + signal(SIGUSR2, SIG_IGN); + mi.operation = MOUSE_MODE; + mi.u.mode.mode = 0; + mi.u.mode.signal = SIGUSR2; + if (ioctl(tty, CONS_MOUSECTL, &mi) != -1) { + signal(SIGUSR2, sysmouse); + mi.operation = MOUSE_SHOW; + ioctl(tty, CONS_MOUSECTL, &mi); + sysm_handler = sysm_process_mouse; + } + } + mouseActive = 1; +} + +void +mouse_end() +{ + if (mouseActive == 0) + return; + if (is_xterm) { + XTERM_OFF; + } + else { + mouse_info_t mi; + mi.operation = MOUSE_MODE; + mi.u.mode.mode = 0; + mi.u.mode.signal = 0; + ioctl(tty, CONS_MOUSECTL, &mi); + } + mouseActive = 0; +} + +#else +/* not GPM nor SYSMOUSE, but use mouse with xterm */ + +void +mouse_init() +{ + if (mouseActive) + return; + if (is_xterm) { + XTERM_ON; + } + mouseActive = 1; +} + +void +mouse_end() +{ + if (mouseActive == 0) + return; + if (is_xterm) { + XTERM_OFF; + } + mouseActive = 0; +} + +#endif /* not USE_GPM nor USE_SYSMOUSE */ + + +void +mouse_active() +{ + if (!mouseActive) + mouse_init(); +} + +void +mouse_inactive() +{ + if (mouseActive && is_xterm) + mouse_end(); +} + +#endif /* MOUSE */ + +void +flush_tty() +{ + fflush(ttyf); +} @@ -0,0 +1,38 @@ +#ifndef TERMS_H +#define TERMS_H + +extern int LINES, COLS; + +extern char DisplayCode; + +#define CODE_ASCII '\0' +#define CODE_EUC 'E' +#define CODE_SJIS 'S' +#define CODE_JIS_n 'n' +#define CODE_JIS_m 'm' +#define CODE_JIS_N 'N' +#define CODE_JIS_j 'j' +#define CODE_JIS_J 'J' +#define CODE_INNER_EUC 'I' + +#define STR_ASCII "US_ASCII" +#define STR_EUC "EUC-JP" +#define STR_SJIS "Shift_JIS" +#define STR_JIS_n "ISO-2022-JP (JIS X 0208 + US_ASCII)" +#define STR_JIS_m "ISO-2022-JP (JIS C 6226 + US_ASCII)" +#define STR_JIS_N "ISO-2022-JP (JIS X 0208 + JIS X 0201)" +#define STR_JIS_j "ISO-2022-JP (JIS C 6226 + JIS X 0201)" +#define STR_JIS_J "ISO-2022-JP (JIS C 6226 + '\033(H')" +#define STR_INNER_EUC "EUC-JP (internal)" + +#if defined(__EMX__)&&!defined(JP_CHARSET) +/* + * Following definitions are valid only for the OS/2 native console + */ +#define CODE_850 '8' /* code page 850 */ +#define CODE_PC 'P' /* another code pages */ +#endif /* __EMX__ */ + +#define CODE_JIS(x) ((x)==CODE_JIS_n||(x)==CODE_JIS_m||(x)==CODE_JIS_N||(x)==CODE_JIS_j||(x)==CODE_JIS_J) + +#endif /* not TERMS_H */ diff --git a/textlist.c b/textlist.c new file mode 100644 index 0000000..2804b19 --- /dev/null +++ b/textlist.c @@ -0,0 +1,139 @@ +/* $Id: textlist.c,v 1.1 2001/11/08 05:15:43 a-ito Exp $ */ +#include "textlist.h" +#include "indep.h" +#include "Str.h" +#include "gc.h" + +/* General doubly linked list */ + +ListItem * +newListItem(void *s, ListItem * n, ListItem * p) +{ + ListItem *it; + it = New(ListItem); + it->ptr = s; + it->next = n; + it->prev = p; + return it; +} + +GeneralList * +newGeneralList() +{ + GeneralList *tl = New(GeneralList); + tl->first = tl->last = NULL; + tl->nitem = 0; + return tl; +} + +void +pushValue(GeneralList * tl, void *s) +{ + ListItem *it; + if (s == NULL) + return; + it = newListItem(s, NULL, tl->last); + if (tl->first == NULL) { + tl->first = it; + tl->last = it; + tl->nitem = 1; + } + else { + tl->last->next = it; + tl->last = it; + tl->nitem++; + } +} + +void * +popValue(GeneralList * tl) +{ + ListItem *f; + + if (tl == NULL || tl->first == NULL) + return NULL; + f = tl->first; + tl->first = f->next; + if (tl->first) + tl->first->prev = NULL; + else + tl->last = NULL; + tl->nitem--; + return f->ptr; +} + +void * +rpopValue(GeneralList * tl) +{ + ListItem *f; + + if (tl == NULL || tl->last == NULL) + return NULL; + f = tl->last; + tl->last = f->prev; + if (tl->last) + tl->last->next = NULL; + else + tl->first = NULL; + tl->nitem--; + return f->ptr; +} + +GeneralList * +appendGeneralList(GeneralList * tl, GeneralList * tl2) +{ + if (tl && tl2) { + if (tl2->first) { + if (tl->last) { + tl->last->next = tl2->first; + tl2->first->prev = tl->last; + tl->last = tl2->last; + tl->nitem += tl2->nitem; + } + else { + tl->first = tl2->first; + tl->last = tl2->last; + tl->nitem = tl2->nitem; + } + } + tl2->first = tl2->last = NULL; + tl2->nitem = 0; + } + + return tl; +} + + +/* Line text list */ + +TextLine * +newTextLine(Str line, int pos) +{ + TextLine *lbuf = New(TextLine); + if (line) + lbuf->line = line; + else + lbuf->line = Strnew(); + lbuf->pos = pos; + return lbuf; +} + +void +appendTextLine(TextLineList * tl, Str line, int pos) +{ + TextLine *lbuf; + + if (tl->last == NULL) { + pushTextLine(tl, newTextLine(Strdup(line), pos)); + } + else { + lbuf = tl->last->ptr; + if (lbuf->line) + Strcat(lbuf->line, line); + else + lbuf->line = line; + lbuf->pos += pos; + } +} + + diff --git a/textlist.h b/textlist.h new file mode 100644 index 0000000..3548ff1 --- /dev/null +++ b/textlist.h @@ -0,0 +1,73 @@ +#ifndef TEXTLIST_H +#define TEXTLIST_H +#include "Str.h" + +/* General doubly linked list */ + +typedef struct _listitem { + void *ptr; + struct _listitem *next; + struct _listitem *prev; +} ListItem; + +typedef struct _generallist { + ListItem *first; + ListItem *last; + short nitem; +} GeneralList; + +extern ListItem *newListItem(void *s, ListItem * n, ListItem * p); +extern GeneralList *newGeneralList(void); +extern void pushValue(GeneralList * tl, void *s); +extern void *popValue(GeneralList * tl); +extern void *rpopValue(GeneralList * tl); +extern GeneralList *appendGeneralList(GeneralList *, GeneralList *); + +/* Text list */ + +typedef struct _textlistitem { + char *ptr; + struct _textlistitem *next; + struct _textlistitem *prev; +} TextListItem; + +typedef struct _textlist { + TextListItem *first; + TextListItem *last; + short nitem; +} TextList; + +#define newTextList() ((TextList *)newGeneralList()) +#define pushText(tl, s) pushValue((GeneralList *)(tl), (void *)allocStr((s)?(s):"",0)) +#define popText(tl) ((char *)popValue((GeneralList *)(tl))) +#define rpopText(tl) ((char *)rpopValue((GeneralList *)(tl))) +#define appendTextList(tl, tl2) ((TextList *)appendGeneralList((GeneralList *)(tl), (GeneralList *)(tl2))) + +/* Line text list */ + +typedef struct _TextLine { + Str line; + short pos; +} TextLine; + +typedef struct _textlinelistitem { + TextLine *ptr; + struct _textlinelistitem *next; + struct _textlinelistitem *prev; +} TextLineListItem; + +typedef struct _textlinelist { + TextLineListItem *first; + TextLineListItem *last; + short nitem; +} TextLineList; + +extern TextLine *newTextLine(Str line, int pos); +extern void appendTextLine(TextLineList * tl, Str line, int pos); +#define newTextLineList() ((TextLineList *)newGeneralList()) +#define pushTextLine(tl,lbuf) pushValue((GeneralList *)(tl),(void *)(lbuf)) +#define popTextLine(tl) ((TextLine *)popValue((GeneralList *)(tl))) +#define rpopTextLine(tl) ((TextLine *)rpopValue((GeneralList *)(tl))) +#define appendTextLineList(tl, tl2) ((TextLineList *)appendGeneralList((GeneralList *)(tl), (GeneralList *)(tl2))) + +#endif /* not TEXTLIST_H */ @@ -0,0 +1,1784 @@ +#include "fm.h" +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include <signal.h> +#include <setjmp.h> +#include <errno.h> + +#include <sys/stat.h> +#ifdef __EMX__ +#include <io.h> +#include <strings.h> +#endif /* __EMX__ */ + +#include "html.h" +#include "Str.h" +#include "myctype.h" +#include "regex.h" + +#ifdef USE_SSL +#ifndef SSLEAY_VERSION_NUMBER +#include <crypto.h> /* SSLEAY_VERSION_NUMBER may be here */ +#endif +#include <err.h> +#endif + +#ifdef __WATT32__ +#define write(a,b,c) write_s(a,b,c) +#endif /* __WATT32__ */ + +#define NOPROXY_NETADDR /* allow IP address for no_proxy */ + +#ifdef INET6 +/* see rc.c, "dns_order" and dnsorders[] */ +int ai_family_order_table[3][3] = +{ + {PF_UNSPEC, PF_UNSPEC, PF_UNSPEC}, /* 0:unspec */ + {PF_INET, PF_INET6, PF_UNSPEC}, /* 1:inet inet6 */ + {PF_INET6, PF_INET, PF_UNSPEC} /* 2:inet6 inet */ +}; +#endif /* INET6 */ + +static JMP_BUF AbortLoading; + +/* XXX: note html.h SCM_ */ +static int + DefaultPort[] = +{ + 80, /* http */ + 70, /* gopher */ + 21, /* ftp */ + 21, /* ftpdir */ + 0, /* local - not defined */ + 0, /* ??? - not defined? */ + 0, /* exec - not defined? */ + 119, /* nntp */ + 119, /* news */ + 0, /* mailto - not defined */ +#ifdef USE_SSL + 443, /* https */ +#endif /* USE_SSL */ +}; + +struct cmdtable schemetable[] = +{ + {"http", SCM_HTTP}, + {"gopher", SCM_GOPHER}, + {"ftp", SCM_FTP}, + {"local", SCM_LOCAL}, + {"file", SCM_LOCAL}, + {"exec", SCM_LOCAL_CGI}, /* ??? */ + {"nntp", SCM_NNTP}, + {"news", SCM_NEWS}, + {"mailto", SCM_MAILTO}, +#ifdef USE_SSL + {"https", SCM_HTTPS}, +#endif /* USE_SSL */ + {NULL, SCM_UNKNOWN}, +}; + +static struct table2 DefaultGuess[] = +{ + {"html", "text/html"}, + {"HTML", "text/html"}, + {"htm", "text/html"}, + {"HTM", "text/html"}, + {"shtml", "text/html"}, + {"SHTML", "text/html"}, + {"gif", "image/gif"}, + {"GIF", "image/gif"}, + {"jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"JPEG", "image/jpeg"}, + {"JPG", "image/jpeg"}, + {"png", "image/png"}, + {"PNG", "image/png"}, + {"xbm", "image/xbm"}, + {"XBM", "image/xbm"}, + {"au", "audio/basic"}, + {"AU", "audio/basic"}, + {"gz", "application/x-gzip"}, + {"Z", "application/x-compress"}, + {"bz2", "application/x-bzip"}, + {"tar", "application/x-tar"}, + {"zip", "application/x-zip"}, + {"lha", "application/x-lha"}, + {"lzh", "application/x-lha"}, + {"LZH", "application/x-lha"}, + {"ps", "application/postscript"}, + {"pdf", "application/pdf"}, + {NULL, NULL} +}; + +static void add_index_file(ParsedURL * pu, URLFile *uf); + +/* #define HTTP_DEFAULT_FILE "/index.html" */ + +#ifndef HTTP_DEFAULT_FILE +#define HTTP_DEFAULT_FILE "/" +#endif /* not HTTP_DEFAULT_FILE */ + +#ifdef SOCK_DEBUG +#include <stdarg.h> + +static void +sock_log(char *message,...) +{ + FILE *f = fopen("zzzsocklog", "a"); + va_list va; + + if (f == NULL) + return; + va_start(va, message); + vfprintf(f, message, va); + fclose(f); +} + +#endif + +static char * +DefaultFile(int scheme) +{ + switch (scheme) { + case SCM_HTTP: +#ifdef USE_SSL + case SCM_HTTPS: +#endif /* USE_SSL */ + return allocStr(HTTP_DEFAULT_FILE, 0); +#ifdef USE_GOPHER + case SCM_GOPHER: + return allocStr("1", 0); +#endif /* USE_GOPHER */ + case SCM_LOCAL: + case SCM_LOCAL_CGI: + case SCM_FTP: + return allocStr("/", 0); + } + return NULL; +} + +static MySignalHandler +KeyAbort(SIGNAL_ARG) +{ + LONGJMP(AbortLoading, 1); +} + +#ifdef USE_SSL +SSL_CTX *ssl_ctx = NULL; + +void +free_ssl_ctx() +{ + if (ssl_ctx != NULL) + SSL_CTX_free(ssl_ctx); +} + +#if SSLEAY_VERSION_NUMBER >= 0x00905100 +#include <rand.h> +void +init_PRNG() +{ + char buffer[256]; + const char *file; + long l; + if (RAND_status()) + return; + if (file = RAND_file_name(buffer, sizeof(buffer))) { +#ifdef USE_EGD + if (RAND_egd(file) > 0) + return; +#endif + RAND_load_file(file, -1); + } + if (RAND_status()) + goto seeded; + srand48((long) time(NULL)); + while (!RAND_status()) { + l = lrand48(); + RAND_seed((unsigned char *)&l, sizeof(long)); + } + seeded: + if (file) + RAND_write_file(file); +} +#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ + +SSL * +openSSLHandle(int sock) +{ + SSL *handle; + char *emsg; + static char *old_ssl_forbid_method = NULL; +#ifdef USE_SSL_VERIFY + static int old_ssl_verify_server = -1; +#endif + + if (! old_ssl_forbid_method || ! ssl_forbid_method || + strcmp(old_ssl_forbid_method, ssl_forbid_method)) { + old_ssl_forbid_method = ssl_forbid_method; +#ifdef USE_SSL_VERIFY + ssl_path_modified = 1; +#else + free_ssl_ctx(); + ssl_ctx = NULL; +#endif + } +#ifdef USE_SSL_VERIFY + if (old_ssl_verify_server != ssl_verify_server) { + old_ssl_verify_server = ssl_verify_server; + ssl_path_modified = 1; + } + if (ssl_path_modified) { + free_ssl_ctx(); + ssl_ctx = NULL; + ssl_path_modified = 0; + } +#endif /* defined(USE_SSL_VERIFY) */ + if (ssl_ctx == NULL) { + int option; +#if SSLEAY_VERSION_NUMBER < 0x0800 + ssl_ctx = SSL_CTX_new(); + X509_set_default_verify_paths(ssl_ctx->cert); +#else /* SSLEAY_VERSION_NUMBER >= 0x0800 */ + SSLeay_add_ssl_algorithms(); + SSL_load_error_strings(); + if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method()))) + goto eend; + option = SSL_OP_ALL; + if (ssl_forbid_method) { + if (strchr(ssl_forbid_method, '2')) option |= SSL_OP_NO_SSLv2; + if (strchr(ssl_forbid_method, '3')) option |= SSL_OP_NO_SSLv3; + if (strchr(ssl_forbid_method, 't')) option |= SSL_OP_NO_TLSv1; + if (strchr(ssl_forbid_method, 'T')) option |= SSL_OP_NO_TLSv1; + } + SSL_CTX_set_options(ssl_ctx, option); +#ifdef USE_SSL_VERIFY + /* derived from openssl-0.9.5/apps/s_{client,cb}.c */ + SSL_CTX_set_verify(ssl_ctx, ssl_verify_server ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + if (ssl_cert_file != NULL && *ssl_cert_file != '\0') { + int ng = 1; + if (SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert_file, SSL_FILETYPE_PEM) > 0) { + char *key_file = (ssl_key_file == NULL || *ssl_key_file == '\0') ? ssl_cert_file : ssl_key_file; + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, key_file, SSL_FILETYPE_PEM) > 0) + if (SSL_CTX_check_private_key(ssl_ctx)) + ng = 0; + } + if (ng) { + free_ssl_ctx(); + goto eend; + } + } + if (SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca_file, ssl_ca_path)) +#endif /* defined(USE_SSL_VERIFY) */ + SSL_CTX_set_default_verify_paths(ssl_ctx); +#endif /* SSLEAY_VERSION_NUMBER >= 0x0800 */ + atexit(free_ssl_ctx); + } + handle = SSL_new(ssl_ctx); + SSL_set_fd(handle, sock); +#if SSLEAY_VERSION_NUMBER >= 0x00905100 + init_PRNG(); +#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */ + if (SSL_connect(handle) <= 0) + goto eend; + return handle; +eend: + emsg = Sprintf("SSL error: %s", ERR_error_string(ERR_get_error(), NULL))->ptr; + disp_err_message(emsg, FALSE); + return NULL; +} + +static void +SSL_write_from_file(SSL * ssl, char *file) +{ + FILE *fd; + int c; + char buf[1]; + fd = fopen(file, "r"); + if (fd != NULL) { + while ((c = fgetc(fd)) != EOF) { + buf[0] = c; + SSL_write(ssl, buf, 1); + } + fclose(fd); + } +} + +#endif /* USE_SSL */ + +static void +write_from_file(int sock, char *file) +{ + FILE *fd; + int c; + char buf[1]; + fd = fopen(file, "r"); + if (fd != NULL) { + while ((c = fgetc(fd)) != EOF) { + buf[0] = c; + write(sock, buf, 1); + } + fclose(fd); + } +} + +ParsedURL * +baseURL(Buffer * buf) +{ + if (buf->bufferprop & BP_NO_URL) { + /* no URL is defined for the buffer */ + return NULL; + } + if (buf->baseURL != NULL) { + /* <BASE> tag is defined in the document */ + return buf->baseURL; + } + else + return &buf->currentURL; +} + +int +openSocket(char *hostname, + char *remoteport_name, + unsigned short remoteport_num) +{ + int sock = -1; +#ifdef INET6 + int *af; + struct addrinfo hints, *res0, *res; + int error; +#else /* not INET6 */ + struct sockaddr_in hostaddr; + struct hostent *entry; + struct protoent *proto; + unsigned short s_port; + int a1, a2, a3, a4; + unsigned long adr; +#endif /* not INET6 */ + MySignalHandler(*trap) (); + + if (fmInitialized) { + message(Sprintf("Opening socket...\n")->ptr, 0, 0); + refresh(); + } + if (SETJMP(AbortLoading) != 0) { +#ifdef SOCK_DEBUG + sock_log("openSocket() failed. reason: user abort\n"); +#endif + if (sock >= 0) + close(sock); + goto error; + } + trap = signal(SIGINT, KeyAbort); + if (fmInitialized) + term_cbreak(); + if (hostname == NULL) { +#ifdef SOCK_DEBUG + sock_log("openSocket() failed. reason: Bad hostname \"%s\"\n", hostname); +#endif + goto error; + } + +#ifdef INET6 + for (af = ai_family_order_table[DNS_order];; af++) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = *af; + hints.ai_socktype = SOCK_STREAM; + if (remoteport_num != 0) { + Str portbuf = Sprintf("%d", remoteport_num); + error = getaddrinfo(hostname, portbuf->ptr, &hints, &res0); + } + else { + error = -1; + } + if (error && remoteport_name && remoteport_name[0] != '\0') { + /* try default port */ + error = getaddrinfo(hostname, remoteport_name, &hints, &res0); + } + if (error) { + if (*af == PF_UNSPEC) { + goto error; + } + /* try next ai family */ + continue; + } + sock = -1; + for (res = res0; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock < 0) { + continue; + } + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + close(sock); + sock = -1; + continue; + } + break; + } + if (sock < 0) { + freeaddrinfo(res0); + if (*af == PF_UNSPEC) { + goto error; + } + /* try next ai family */ + continue; + } + freeaddrinfo(res0); + break; + } +#else /* not INET6 */ + s_port = htons(remoteport_num); + bzero((char *) &hostaddr, sizeof(struct sockaddr_in)); + if ((proto = getprotobyname("tcp")) == NULL) { + /* protocol number of TCP is 6 */ + proto = New(struct protoent); + proto->p_proto = 6; + } + if ((sock = socket(AF_INET, SOCK_STREAM, proto->p_proto)) < 0) { +#ifdef SOCK_DEBUG + sock_log("openSocket: socket() failed. reason: %s\n", strerror(errno)); +#endif + goto error; + } + regexCompile("[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*", 0); + if (regexMatch(hostname, 0, 1)) { + sscanf(hostname, "%d.%d.%d.%d", &a1, &a2, &a3, &a4); + adr = htonl((a1 << 24) | (a2 << 16) | (a3 << 8) | a4); + bcopy((void *) &adr, (void *) &hostaddr.sin_addr, sizeof(long)); + hostaddr.sin_family = AF_INET; + hostaddr.sin_port = s_port; + if (fmInitialized) { + message(Sprintf("Connecting to %s\n", hostname)->ptr, 0, 0); + refresh(); + } + if (connect(sock, (struct sockaddr *) &hostaddr, + sizeof(struct sockaddr_in)) < 0) { +#ifdef SOCK_DEBUG + sock_log("openSocket: connect() failed. reason: %s\n", strerror(errno)); +#endif + goto error; + } + } + else { + char **h_addr_list; + int result; + if (fmInitialized) { + message(Sprintf("Performing hostname lookup on %s\n", hostname)->ptr, 0, 0); + refresh(); + } + if ((entry = gethostbyname(hostname)) == NULL) { +#ifdef SOCK_DEBUG + sock_log("openSocket: gethostbyname() failed. reason: %s\n", strerror(errno)); +#endif + goto error; + } + hostaddr.sin_family = AF_INET; + hostaddr.sin_port = s_port; + for (h_addr_list = entry->h_addr_list; *h_addr_list; h_addr_list++) { + bcopy((void *) h_addr_list[0], (void *) &hostaddr.sin_addr, entry->h_length); +#ifdef SOCK_DEBUG + adr = ntohl(*(long *) &hostaddr.sin_addr); + sock_log("openSocket: connecting %d.%d.%d.%d\n", + (adr >> 24) & 0xff, + (adr >> 16) & 0xff, + (adr >> 8) & 0xff, + adr & 0xff); +#endif + if (fmInitialized) { + message(Sprintf("Connecting to %s\n", hostname)->ptr, 0, 0); + refresh(); + } + if ((result = connect(sock, (struct sockaddr *) &hostaddr, + sizeof(struct sockaddr_in))) == 0) { + break; + } +#ifdef SOCK_DEBUG + else { + sock_log("openSocket: connect() failed. reason: %s\n", strerror(errno)); + } +#endif + } + if (result < 0) { + goto error; + } + } +#endif /* not INET6 */ + + if (fmInitialized) + term_raw(); + signal(SIGINT, trap); + return sock; + error: + if (fmInitialized) + term_raw(); + signal(SIGINT, trap); + + return -1; + +} + + +#define COPYPATH_SPC_ALLOW 0 +#define COPYPATH_SPC_IGNORE 1 +#define COPYPATH_SPC_REPLACE 2 + +static char * +copyPath(char *orgpath, int length, int option) +{ + Str tmp = Strnew(); + while (*orgpath && length != 0) { + if (IS_SPACE(*orgpath)) { + switch(option) { + case COPYPATH_SPC_ALLOW: + Strcat_char(tmp,*orgpath); + break; + case COPYPATH_SPC_IGNORE: + /* do nothing */ + break; + case COPYPATH_SPC_REPLACE: + Strcat_charp(tmp,"%20"); + break; + } + } + else + Strcat_char(tmp,*orgpath); + orgpath++; + length--; + } + return tmp->ptr; +} + +void +parseURL(char *url, ParsedURL * p_url, ParsedURL * current) +{ + char *p, *q; + Str tmp; +#ifdef __EMX__ + int i; +#endif + + p = url; + p_url->scheme = SCM_MISSING; + p_url->port = 0; + p_url->user = NULL; + p_url->pass = NULL; + p_url->host = NULL; + p_url->is_nocache = 0; + p_url->file = NULL; + p_url->label = NULL; + + if (*url == '#') { /* label only */ + if (current) + copyParsedURL(p_url, current); + goto do_label; + } +#ifdef __EMX__ + for (i = 17; i > 0; i -= 10) + if (!strncmp(url, "file://localhost/", i)) { + p_url->scheme = SCM_LOCAL; + p += i; + url += i; + break; + } + if (*url == '/' && IS_ALPHA(url[1]) && (url[2] == ':' || url[2] == '|')) { + ++p; + ++url; + } + if (IS_ALPHA(*url) && (url[1] == ':' || url[1] == '|')) { + p_url->scheme = SCM_LOCAL; + p_url->file = p = allocStr(url, 0); + if (p[1] == '|') + p[1] = ':'; + } + if (p_url->scheme == SCM_LOCAL) + goto analyze_file; +#endif + /* search for scheme */ + p_url->scheme = getURLScheme(&p); + if (p_url->scheme == SCM_MISSING) { /* scheme not found */ + if (current) + copyParsedURL(p_url, current); + else + p_url->scheme = SCM_LOCAL; + p = url; + goto analyze_file; + } + /* get host and port */ + if (p[0] != '/' || p[1] != '/') { /* scheme:foo or * scheme:/foo */ + p_url->host = NULL; + if (p_url->scheme != SCM_UNKNOWN) + p_url->port = DefaultPort[p_url->scheme]; + else + p_url->port = 0; + goto analyze_file; + } + if (p_url->scheme == SCM_LOCAL) { /* file://foo */ +#ifdef __EMX__ + p += 2; + goto analyze_file; +#else + if (p[2] == '/' || p[2] == '~') { /* file:///foo */ + p += 2; /* file://~user */ + goto analyze_file; + } +#endif +#ifdef CYGWIN + goto analyze_file; /* file://DRIVE/foo or * + * file://machine/share/foo */ +#endif /* CYGWIN */ + } + p += 2; /* scheme://foo */ + analyze_url: + q = p; + while (*p && *p != ':' && *p != '/' && *p != '@') { +#ifdef INET6 + if (*p == '[') { /* rfc2732 compliance */ + char *p_colon = NULL; + do { + p++; + if ((p_colon == NULL) && (*p == ':')) + p_colon = p; + } while (*p && (IS_ALNUM(*p) || *p == ':' || *p == '.')); + if (*p == ']') { + p++; + break; + } + else if (p_colon) { + p = p_colon; + break; + } + } +#endif + p++; + } + switch (*p) { + case '\0': /* scheme://host */ + /* scheme://user@host */ + /* scheme://user:pass@host */ + p_url->host = copyPath(q, -1, COPYPATH_SPC_IGNORE); + p_url->port = DefaultPort[p_url->scheme]; + goto analyze_file; + case ':': + p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE); + q = ++p; + while (*p && *p != '/' && *p != '@') + p++; + if (*p == '@') { /* scheme://user:pass@... */ + p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW); + q = ++p; + p_url->user = p_url->host; + p_url->host = NULL; + goto analyze_url; + } + tmp = Strnew_charp_n(q, p - q); + p_url->port = atoi(tmp->ptr); + if (*p == '\0') { /* scheme://host:port */ + /* scheme://user@host:port */ + /* scheme://user:pass@host:port */ + p_url->file = DefaultFile(p_url->scheme); + p_url->label = NULL; + return; + } + break; + case '@': /* scheme://user@... */ + p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE); + q = ++p; + goto analyze_url; + case '/': /* scheme://host/... */ + /* scheme://user@host/... */ + /* scheme://user:pass@host/... */ + p_url->host = copyPath(q, p - q, COPYPATH_SPC_IGNORE); + p_url->port = DefaultPort[p_url->scheme]; + break; + } +#ifdef INET6 + /* rfc2732 compliance */ + if (p_url->host != NULL && p_url->host[0] == '[' && + p_url->host[strlen(p_url->host)-1] == ']' ) { + p_url->host[strlen(p_url->host)-1] = '\0'; + ++(p_url->host); + } +#endif + analyze_file: + if (p_url->scheme == SCM_LOCAL && p_url->user == NULL && + p_url->host != NULL && strcmp(p_url->host, "localhost")) { + p_url->scheme = SCM_FTP; /* ftp://host/... */ + if (p_url->port == 0) + p_url->port = DefaultPort[SCM_FTP]; + } + + q = p; +#ifdef USE_GOPHER + if (p_url->scheme == SCM_GOPHER) { + if (*q == '/') + q++; + if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/') + q++; + } +#endif /* USE_GOPHER */ + if (*p == '/') + p++; + if (*p == '\0') { /* scheme://host[:port]/ */ + p_url->file = DefaultFile(p_url->scheme); + p_url->label = NULL; + return; + } +#ifdef USE_GOPHER + if (p_url->scheme == SCM_GOPHER && *p == 'R') { + p++; + tmp = Strnew(); + Strcat_char(tmp, *(p++)); + while (*p && *p != '/') + p++; + Strcat_charp(tmp, p); + while (*p) + p++; + p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE); + } + else +#endif /* USE_GOPHER */ + { + char *cgi = strchr(p, '?'); + again: + while (*p && *p != '#') + p++; + if (*p == '#' && p_url->scheme == SCM_LOCAL) { + /* + * According to RFC2396, # means the beginning of + * URI-reference, and # should be escaped. But, + * if the scheme is SCM_LOCAL, the special + * treatment will apply to # for convinience. + */ + if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) { + /* + * # comes as the first character of the file name + * that means, # is not a label but a part of the file + * name. + */ + p++; + goto again; + } + else if (*(p + 1) == '\0') { + /* + * # comes as the last character of the file name that + * means, # is not a label but a part of the file + * name. + */ + p++; + } + } + if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING) + p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW); + else + p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE); + } + do_label: + if (p_url->scheme == SCM_MISSING) { + p_url->scheme = SCM_LOCAL; + p_url->file = allocStr(p, 0); + p_url->label = NULL; + } + else if (*p == '#') + p_url->label = allocStr(p + 1, 0); + else + p_url->label = NULL; +} + +#define initParsedURL(p) bzero(p,sizeof(ParsedURL)) + +void +copyParsedURL(ParsedURL * p, ParsedURL * q) +{ + p->scheme = q->scheme; + p->port = q->port; + p->is_nocache = q->is_nocache; + if (q->user) + p->user = allocStr(q->user, 0); + else + p->user = NULL; + if (q->pass) + p->pass = allocStr(q->pass, 0); + else + p->pass = NULL; + if (q->host) + p->host = allocStr(q->host, 0); + else + p->host = NULL; + if (q->file) + p->file = allocStr(q->file, 0); + else + p->file = NULL; + if (q->label) + p->label = allocStr(q->label, 0); + else + p->label = NULL; +} + +void +parseURL2(char *url, ParsedURL * pu, ParsedURL * current) +{ + char *p, *q; + Str tmp; + + parseURL(url, pu, current); + if (pu->scheme == SCM_MAILTO) + return; + + if (pu->scheme == SCM_LOCAL) + pu->file = expandName(pu->file); + + if (current && pu->scheme == current->scheme) { + if (pu->user == NULL) { + pu->user = current->user; + } + if (pu->pass == NULL) { + pu->pass = current->pass; + } + if (pu->host == NULL) { + pu->host = current->host; + } + if (pu->file) { + if ( +#ifdef USE_GOPHER + pu->scheme != SCM_GOPHER && +#endif /* USE_GOPHER */ +#ifdef USE_NNTP + pu->scheme != SCM_NEWS && +#endif /* USE_NNTP */ + pu->file[0] != '/' +#ifdef __EMX__ + && !(pu->scheme == SCM_LOCAL && IS_ALPHA(pu->file[0]) && pu->file[1] == ':') +#endif + ) { + p = pu->file; + if (current->file) { + tmp = Strnew_charp(current->file); + if ((q = strchr(tmp->ptr, '?')) != NULL) + Strshrink(tmp, (tmp->ptr + tmp->length) - q); + while (tmp->length > 0) { + if (Strlastchar(tmp) == '/') + break; + Strshrink(tmp, 1); + } + Strcat_charp(tmp, p); + pu->file = allocStr(tmp->ptr, 0); + } + } +#ifdef USE_GOPHER + else if (pu->scheme == SCM_GOPHER && + pu->file[0] == '/') { + p = pu->file; + pu->file = allocStr(p + 1, 0); + } +#endif /* USE_GOPHER */ + } + else if (pu->label) { + /* pu has only label */ + pu->file = current->file; + } + } + if (pu->file) { +#ifdef __EMX__ + if (pu->scheme == SCM_LOCAL) { + if (strncmp(pu->file, "/$LIB/", 6)) { + char *arg, abs[_MAX_PATH], tmp[_MAX_PATH]; + + if (!(arg = strchr(strcpy(tmp, pu->file), '?'))) { + _abspath(abs, tmp, _MAX_PATH); + pu->file = cleanupName(abs); + } + else { + *arg = 0; + _abspath(abs, tmp, _MAX_PATH); + *arg = '?'; + pu->file = cleanupName(strcat(abs, arg)); + } + } + } +#else + if (pu->scheme == SCM_LOCAL && pu->file[0] != '/' && + strcmp(pu->file, "-")) { + tmp = Strnew_charp(CurrentDir); + if (Strlastchar(tmp) != '/') + Strcat_char(tmp, '/'); + Strcat_charp(tmp, pu->file); + pu->file = cleanupName(tmp->ptr); + } +#endif + else if ((pu->scheme == SCM_HTTP +#ifdef USE_SSL + || pu->scheme == SCM_HTTPS +#endif + ) && pu->file[0] == '/') { + char *p = &pu->file[1]; + int s = getURLScheme(&p); + if (s == SCM_MISSING || s == SCM_UNKNOWN) + pu->file = cleanupName(pu->file); + } else if ( +#ifdef USE_GOPHER + pu->scheme != SCM_GOPHER && +#endif /* USE_GOPHER */ +#ifdef USE_NNTP + pu->scheme != SCM_NEWS && +#endif /* USE_NNTP */ + pu->file[0] == '/') { + pu->file = cleanupName(pu->file); + } + } +} + +static Str +_parsedURL2Str(ParsedURL * pu, int pass) +{ + Str tmp = Strnew(); + static char *scheme_str[] = + { + "http", "gopher", "ftp", "ftp", "file", "file", "exec", "nntp", "news", "mailto", +#ifdef USE_SSL + "https", +#endif /* USE_SSL */ + }; + + if (pu->scheme == SCM_UNKNOWN || pu->scheme == SCM_MISSING) { + return Strnew_charp("???"); + } + if (pu->host == NULL && pu->file == NULL && pu->label != NULL) { + /* local label */ + return Sprintf("#%s", pu->label); + } + if (pu->scheme == SCM_LOCAL && ! strcmp(pu->file, "-")) { + tmp = Strnew_charp("-"); + if (pu->label) { + Strcat_char(tmp, '#'); + Strcat_charp(tmp, pu->label); + } + return tmp; + } + tmp = Strnew_charp(scheme_str[pu->scheme]); + Strcat_char(tmp, ':'); + if (pu->scheme == SCM_MAILTO) { + Strcat_charp(tmp, pu->file); + return tmp; + } +#ifdef USE_NNTP + if (pu->scheme != SCM_NEWS) +#endif /* USE_NNTP */ + { + Strcat_charp(tmp, "//"); + } + if (pu->user) { + Strcat_charp(tmp, pu->user); + if (pass && pu->pass) { + Strcat_char(tmp, ':'); + Strcat_charp(tmp, pu->pass); + } + Strcat_char(tmp, '@'); + } + if (pu->host) { + Strcat_charp(tmp, pu->host); + if (pu->port != DefaultPort[pu->scheme]) { + Strcat_char(tmp, ':'); + Strcat(tmp, Sprintf("%d", pu->port)); + } + } + if ( +#ifdef USE_NNTP + pu->scheme != SCM_NEWS && +#endif /* USE_NNTP */ +#ifdef __EMX__ + (pu->file == NULL || (pu->file[0] != '/' && pu->file[1] != ':'))) +#else + (pu->file == NULL || pu->file[0] != '/')) +#endif + Strcat_char(tmp, '/'); + Strcat_charp(tmp, pu->file); + if (pu->label) { + Strcat_char(tmp, '#'); + Strcat_charp(tmp, pu->label); + } + return tmp; +} + +Str +parsedURL2Str(ParsedURL * pu) +{ + return _parsedURL2Str(pu, FALSE); +} + +int +getURLScheme(char **url) +{ + char *p = *url, *q; + int i; + int scheme = SCM_MISSING; + + while (*p && (IS_ALPHA(*p) || *p == '.' || *p == '+' || *p == '-')) + p++; + if (*p == ':') { /* scheme found */ + scheme = SCM_UNKNOWN; + for (i = 0; (q = schemetable[i].cmdname) != NULL; i++) { + int len = strlen(q); + if (!strncasecmp(q, *url, len) && (*url)[len] == ':') { + scheme = schemetable[i].cmd; + *url = p + 1; + break; + } + } + } + return scheme; +} + +static char * +otherinfo(ParsedURL * target, ParsedURL * current, char *referer) +{ + Str s = Strnew(); + + Strcat_charp(s, "User-Agent: "); + if (UserAgent == NULL || *UserAgent == '\0') + Strcat_charp(s, version); + else + Strcat_charp(s, UserAgent); + Strcat_charp(s, "\r\n"); + Strcat_charp(s, "Accept: text/*, image/*, audio/*, application/*\r\n"); + Strcat_charp(s, "Accept-Encoding: gzip, compress, bzip, bzip2, deflate\r\n"); + Strcat_charp(s, "Accept-Language: "); + if (AcceptLang != NULL && *AcceptLang != '\0') { + Strcat_charp(s, AcceptLang); + Strcat_charp(s, "\r\n"); + } + else { +#if LANG == JA + Strcat_charp(s, "ja; q=1.0, en; q=0.5\r\n"); +#else /* LANG != JA (must be EN) */ + Strcat_charp(s, "en; q=1.0\r\n"); +#endif /* LANG != JA */ + } + if (target->host) { + Strcat_charp(s, "Host: "); + Strcat_charp(s, target->host); + if (target->port != DefaultPort[target->scheme]) + Strcat(s, Sprintf(":%d", target->port)); + Strcat_charp(s, "\r\n"); + } + if (target->is_nocache) { + Strcat_charp(s, "Pragma: no-cache\r\n"); + Strcat_charp(s, "Cache-control: no-cache\r\n"); + } + if (!NoSendReferer) { + if (referer == NULL && current && current->scheme != SCM_LOCAL && + (current->scheme != SCM_FTP || + (current->user == NULL && current->pass == NULL))) { + Strcat_charp(s, "Referer: "); + Strcat(s, parsedURL2Str(current)); + Strcat_charp(s, "\r\n"); + } + else if (referer != NULL && referer != NO_REFERER) { + Strcat_charp(s, "Referer: "); + Strcat_charp(s, referer); + Strcat_charp(s, "\r\n"); + } + } + return s->ptr; +} + +Str +HTTPrequest(ParsedURL * pu, ParsedURL * current, HRequest * hr, TextList * extra) +{ + Str tmp; + TextListItem *i; +#ifdef USE_COOKIE + Str cookie; +#endif /* USE_COOKIE */ + switch (hr->command) { + case HR_COMMAND_CONNECT: + tmp = Strnew_charp("CONNECT "); + break; + case HR_COMMAND_POST: + tmp = Strnew_charp("POST "); + break; + case HR_COMMAND_HEAD: + tmp = Strnew_charp("HEAD "); + break; + case HR_COMMAND_GET: + default: + tmp = Strnew_charp("GET "); + } + if (hr->command == HR_COMMAND_CONNECT) { + Strcat_charp(tmp, pu->host); + Strcat(tmp, Sprintf(":%d", pu->port)); + } + else if (hr->flag & HR_FLAG_LOCAL) { + Strcat_charp(tmp, pu->file); + } + else { + Strcat(tmp, _parsedURL2Str(pu, TRUE)); + } + Strcat_charp(tmp, " HTTP/1.0\r\n"); + if (hr->referer == NO_REFERER) + Strcat_charp(tmp, otherinfo(pu, NULL, NULL)); + else + Strcat_charp(tmp, otherinfo(pu, current, hr->referer)); + if (extra != NULL) + for (i = extra->first; i != NULL; i = i->next) + Strcat_charp(tmp, i->ptr); +#ifdef USE_COOKIE + if (hr->command != HR_COMMAND_CONNECT && + use_cookie && (cookie = find_cookie(pu))) { + Strcat_charp(tmp, "Cookie: "); + Strcat(tmp, cookie); + Strcat_charp(tmp, "\r\n"); + /* [DRAFT 12] s. 10.1 */ + if (cookie->ptr[0] != '$') + Strcat_charp(tmp, "Cookie2: $Version=\"1\"\r\n"); + } +#endif /* USE_COOKIE */ + if (hr->command == HR_COMMAND_POST) { + if (hr->request->enctype == FORM_ENCTYPE_MULTIPART) { + Strcat_charp(tmp, "Content-type: multipart/form-data; boundary="); + Strcat_charp(tmp, hr->request->boundary); + Strcat_charp(tmp, "\r\n"); + Strcat(tmp, Sprintf("Content-length: %ld\r\n", hr->request->length)); + Strcat_charp(tmp, "\r\n"); + } + else { + Strcat_charp(tmp, "Content-type: application/x-www-form-urlencoded\r\n"); + Strcat(tmp, Sprintf("Content-length: %ld\r\n", hr->request->length)); + Strcat_charp(tmp, "\r\n"); + Strcat_charp(tmp, hr->request->body); + Strcat_charp(tmp, "\r\n"); + } + } + else + Strcat_charp(tmp, "\r\n"); +#ifdef DEBUG + fprintf(stderr, "HTTPrequest: [ %s ]\n\n", tmp->ptr); +#endif /* DEBUG */ + return tmp; +} + +void +init_stream(URLFile *uf, int scheme, InputStream stream) +{ + uf->stream = stream; + uf->scheme = scheme; + uf->encoding = ENC_7BIT; + uf->is_cgi = FALSE; + uf->compression = 0; + uf->guess_type = NULL; + uf->ext = NULL; +} + +static InputStream +openFTPStream(ParsedURL * pu) +{ + return newFileStream(openFTP(pu), closeFTP); +} + +URLFile +openURL(char *url, ParsedURL * pu, ParsedURL * current, + URLOption * option, FormList * request, TextList * extra_header, + URLFile * ouf, unsigned char *status) +{ + Str tmp = Strnew(); + int sock; + char *p, *q; + URLFile uf; + HRequest hr; +#ifdef USE_SSL + SSL *sslh; +#endif /* USE_SSL */ +#ifdef USE_NNTP + FILE *fw; + int i; + char *r; + InputStream stream; +#endif /* USE_NNTP */ + + if (ouf) { + uf = *ouf; + } + else { + init_stream(&uf, SCM_MISSING, NULL); + } + + retry: + parseURL2(url, pu, current); + if (pu->scheme == SCM_LOCAL && pu->file == NULL) { + if (pu->label != NULL) { + /* #hogege is not a label but a filename */ + Str tmp2 = Strnew_charp("#"); + Strcat_charp(tmp2, pu->label); + pu->file = tmp2->ptr; + pu->label = NULL; + } + else { + /* given URL must be null string */ +#ifdef SOCK_DEBUG + sock_log("given URL must be null string\n"); +#endif + return uf; + } + } + + uf.scheme = pu->scheme; + pu->is_nocache = (option->flag & RG_NOCACHE); + uf.ext = filename_extension(pu->file, 1); + + hr.command = HR_COMMAND_GET; + hr.flag = 0; + hr.referer = option->referer; + hr.request = request; + + switch (pu->scheme) { + case SCM_LOCAL: + case SCM_LOCAL_CGI: + if (request && request->body) { + /* local CGI: POST */ + uf.stream = newFileStream(localcgi_post(pu->file, request, option->referer), + (void (*)()) pclose); + if (uf.stream == NULL) + goto ordinary_local_file; + uf.is_cgi = TRUE; + uf.scheme = SCM_LOCAL_CGI; + } + else if ((p = strchr(pu->file, '?')) != NULL) { + /* lodal CGI: GET */ + for (q = pu->file; *q && *q != '?'; q++) + Strcat_char(tmp, *q); + uf.stream = newFileStream(localcgi_get(tmp->ptr, p + 1, option->referer), + (void (*)()) pclose); + if (uf.stream == NULL) { + pu->file = tmp->ptr; + goto ordinary_local_file; + } + uf.is_cgi = TRUE; + uf.scheme = SCM_LOCAL_CGI; + } +#ifdef __EMX__ + else if (!strncmp(pu->file + strlen(pu->file) - 4, ".cmd", 4)) +#else + else if (!strncmp(pu->file + strlen(pu->file) - 4, ".cgi", 4)) +#endif + { + /* lodal CGI: GET */ + uf.stream = newFileStream(localcgi_get(pu->file, "", option->referer), + (void (*)()) pclose); + if (uf.stream == NULL) + goto ordinary_local_file; + uf.is_cgi = TRUE; + uf.scheme = SCM_LOCAL_CGI; + } + else { + ordinary_local_file: + examineFile(pu->file, &uf); + } + if (uf.stream == NULL) { + if (dir_exist(pu->file)) { + add_index_file(pu, &uf); + if (uf.stream == NULL) + return uf; + } + else if (document_root != NULL) { + Strcat_charp(tmp, document_root); + if (Strlastchar(tmp) != '/' && pu->file[0] != '/') + Strcat_char(tmp, '/'); + Strcat_charp(tmp, pu->file); + tmp = Strnew_charp(cleanupName(tmp->ptr)); + if (dir_exist(tmp->ptr)) { + pu->file = tmp->ptr; + add_index_file(pu, &uf); + if (uf.stream == NULL) { + return uf; + } + } + else { + examineFile(tmp->ptr, &uf); + if (uf.stream) + pu->file = tmp->ptr; + } + } + } + if (uf.stream == NULL && retryAsHttp && url[0] != '/') { + char *u = url; + int scheme = getURLScheme(&u); + if (scheme == SCM_MISSING || scheme == SCM_UNKNOWN) { + /* retry it as "http://" */ + url = Strnew_m_charp("http://", url, NULL)->ptr; + goto retry; + } + } + return uf; + case SCM_FTP: + if (pu->file == NULL) + pu->file = allocStr("/", 0); + if (non_null(FTP_proxy) && + !Do_not_use_proxy && + pu->host != NULL && + !check_no_proxy(pu->host)) { + sock = openSocket(FTP_proxy_parsed.host, + schemetable[FTP_proxy_parsed.scheme].cmdname, + FTP_proxy_parsed.port); + if (sock < 0) + return uf; + uf.scheme = SCM_HTTP; + tmp = HTTPrequest(pu, current, &hr, extra_header); + write(sock, tmp->ptr, tmp->length); + } + else { + uf.stream = openFTPStream(pu); + uf.scheme = pu->scheme; + return uf; + } + break; + case SCM_HTTP: +#ifdef USE_SSL + case SCM_HTTPS: +#endif /* USE_SSL */ + if (pu->file == NULL) + pu->file = allocStr("/", 0); + if (request && request->method == FORM_METHOD_POST && request->body) + hr.command = HR_COMMAND_POST; + if (request && request->method == FORM_METHOD_HEAD) + hr.command = HR_COMMAND_HEAD; + if (non_null(HTTP_proxy) && + !Do_not_use_proxy && + pu->host != NULL && + !check_no_proxy(pu->host)) { + char *save_label; +#ifdef USE_SSL + if (pu->scheme == SCM_HTTPS && *status == HTST_CONNECT) { + sock = ssl_socket_of(ouf->stream); + if (!(sslh = openSSLHandle(sock))) { + *status = HTST_MISSING; + return uf; + } + } + else { + sock = openSocket(HTTP_proxy_parsed.host, + schemetable[HTTP_proxy_parsed.scheme].cmdname, + HTTP_proxy_parsed.port); + sslh = NULL; + } +#else + sock = openSocket(HTTP_proxy_parsed.host, + schemetable[HTTP_proxy_parsed.scheme].cmdname, + HTTP_proxy_parsed.port); +#endif + if (sock < 0) { +#ifdef SOCK_DEBUG + sock_log("Can't open socket\n"); +#endif + return uf; + } + save_label = pu->label; + pu->label = NULL; +#ifdef USE_SSL + if (pu->scheme == SCM_HTTPS) { + if (*status == HTST_NORMAL) { + hr.command = HR_COMMAND_CONNECT; + tmp = HTTPrequest(pu, current, &hr, NULL); + *status = HTST_CONNECT; + } + else { + hr.flag |= HR_FLAG_LOCAL; + tmp = HTTPrequest(pu, current, &hr, extra_header); + *status = HTST_NORMAL; + } + } + else +#endif /* USE_SSL */ + { + tmp = HTTPrequest(pu, current, &hr, extra_header); + *status = HTST_NORMAL; + pu->label = save_label; + } + } + else { + sock = openSocket(pu->host, + schemetable[pu->scheme].cmdname, + pu->port); + if (sock < 0) { + *status = HTST_MISSING; + return uf; + } +#ifdef USE_SSL + if (pu->scheme == SCM_HTTPS) { + if (!(sslh = openSSLHandle(sock))) { + *status = HTST_MISSING; + return uf; + } + } +#endif /* USE_SSL */ + hr.flag |= HR_FLAG_LOCAL; + tmp = HTTPrequest(pu, current, &hr, extra_header); + *status = HTST_NORMAL; + } +#ifdef USE_SSL + if (pu->scheme == SCM_HTTPS) { + uf.stream = newSSLStream(sslh, sock); + if (sslh) + SSL_write(sslh, tmp->ptr, tmp->length); + else + write(sock, tmp->ptr, tmp->length); + if (hr.command == HR_COMMAND_POST && + request->enctype == FORM_ENCTYPE_MULTIPART) { + if (sslh) + SSL_write_from_file(sslh, request->body); + else + write_from_file(sock, request->body); + } + return uf; + } + else +#endif /* USE_SSL */ + { + write(sock, tmp->ptr, tmp->length); +#ifdef HTTP_DEBUG + { + FILE *ff = fopen("zzrequest", "a"); + fwrite(tmp->ptr, sizeof(char), tmp->length, ff); + fclose(ff); + } +#endif /* HTTP_DEBUG */ + if (hr.command == HR_COMMAND_POST && + request->enctype == FORM_ENCTYPE_MULTIPART) + write_from_file(sock, request->body); + } + break; +#ifdef USE_GOPHER + case SCM_GOPHER: + if (non_null(GOPHER_proxy) && + !Do_not_use_proxy && + pu->host != NULL && + !check_no_proxy(pu->host)) { + sock = openSocket(GOPHER_proxy_parsed.host, + schemetable[GOPHER_proxy_parsed.scheme].cmdname, + GOPHER_proxy_parsed.port); + if (sock < 0) + return uf; + uf.scheme = SCM_HTTP; + tmp = HTTPrequest(pu, current, &hr, extra_header); + } + else { + sock = openSocket(pu->host, + schemetable[pu->scheme].cmdname, + pu->port); + if (sock < 0) + return uf; + if (pu->file == NULL) + pu->file = "1"; + tmp = Strnew_charp(pu->file); + Strcat_char(tmp, '\n'); + } + write(sock, tmp->ptr, tmp->length); + break; +#endif /* USE_GOPHER */ +#ifdef USE_NNTP + case SCM_NNTP: + /* nntp://<host>:<port>/<newsgroup-name>/<article-number> */ + case SCM_NEWS: + if (pu->scheme == SCM_NNTP) { + p = pu->host; + } else { + p = getenv("NNTPSERVER"); + } + r = getenv("NNTPMODE"); + if (p == NULL) + return uf; + sock = openSocket(p, "nntp", pu->port); + if (sock < 0) + return uf; + stream = newInputStream(sock); + fw = fdopen(sock, "wb"); + if (stream == NULL || fw == NULL) + return uf; + tmp = StrISgets(stream); + if (tmp->length == 0) + goto nntp_error; + sscanf(tmp->ptr, "%d", &i); + if (i != 200 && i != 201) + goto nntp_error; + if (r && *r != '\0') { + fprintf(fw, "MODE %s\r\n", r); + fflush(fw); + tmp = StrISgets(stream); + if (tmp->length == 0) + goto nntp_error; + sscanf(tmp->ptr, "%d", &i); + if (i != 200 && i != 201) + goto nntp_error; + } + if (pu->scheme == SCM_NNTP) { + char *group; + if (pu->file == NULL || *pu->file == '\0') + goto nntp_error; + /* first char of pu->file is '/' */ + group = Strnew_charp(pu->file + 1)->ptr; + p = strchr(group, '/'); + if (p == NULL) + goto nntp_error; + *p++ = '\0'; + fprintf(fw, "GROUP %s\r\n", group); + fflush(fw); + tmp = StrISgets(stream); + if (tmp->length == 0) { + goto nntp_error; + } + sscanf(tmp->ptr, "%d", &i); + if (i != 211) + goto nntp_error; + fprintf(fw, "ARTICLE %s\r\n", p); + } else { + fprintf(fw, "ARTICLE <%s>\r\n", pu->file); + } + fflush(fw); + tmp = StrISgets(stream); + if (tmp->length == 0) + goto nntp_error; + sscanf(tmp->ptr, "%d", &i); + if (i != 220) + goto nntp_error; + uf.scheme = SCM_NEWS; /* XXX */ + uf.stream = stream; + return uf; + nntp_error: + ISclose(stream); + fclose(fw); + return uf; +#endif /* USE_NNTP */ + case SCM_UNKNOWN: + default: + return uf; + } + uf.stream = newInputStream(sock); + return uf; +} + +/* add index_file if exists */ +static void +add_index_file(ParsedURL * pu, URLFile *uf) +{ + Str tmp = Strnew_charp(pu->file); + + if (index_file == NULL || index_file[0] == '\0') { + uf->stream = NULL; + return; + } + Strcat_m_charp(tmp, "/", index_file, NULL); + tmp->ptr = cleanupName(tmp->ptr); + examineFile(tmp->ptr, uf); + if (uf->stream == NULL) + return; + pu->file = tmp->ptr; + return; +} + +char * +guessContentTypeFromTable(struct table2 *table, char *filename) +{ + char *p; + if (table == NULL) + return NULL; + p = &filename[strlen(filename) - 1]; + while (filename < p && *p != '.') + p--; + if (p == filename) + return NULL; + p++; + while (table->item1) { + if (!strcmp(p, table->item1)) + return table->item2; + table++; + } + return NULL; +} + +char * +guessContentType(char *filename) +{ + char *ret; + int i; + + if (filename == NULL) + return NULL; + if (mimetypes_list == NULL) + goto no_user_mimetypes; + + for (i = 0; i < mimetypes_list->nitem; i++) { + if ((ret = guessContentTypeFromTable(UserMimeTypes[i], filename)) != NULL) + return ret; + } + + no_user_mimetypes: + return guessContentTypeFromTable(DefaultGuess, filename); +} + +TextList * +make_domain_list(char *domain_list) +{ + char *p; + Str tmp; + TextList *domains = NULL; + + p = domain_list; + tmp = Strnew_size(64); + while (*p) { + while (*p && IS_SPACE(*p)) + p++; + Strclear(tmp); + while (*p && !IS_SPACE(*p) && *p != ',') + Strcat_char(tmp, *p++); + if (tmp->length > 0) { + if (domains == NULL) + domains = newTextList(); + pushText(domains, tmp->ptr); + } + while (*p && IS_SPACE(*p)) + p++; + if (*p == ',') + p++; + } + return domains; +} + +static int +domain_match(char *pat, char *domain) +{ + if (domain == NULL) + return 0; + if (*pat == '.') + pat++; + for (;;) { + if (!strcasecmp(pat, domain)) + return 1; + domain = strchr(domain, '.'); + if (domain == NULL) + return 0; + domain++; + } +} + +int +check_no_proxy(char *domain) +{ + TextListItem *tl; + + if (NO_proxy_domains == NULL || NO_proxy_domains->nitem == 0 || + domain == NULL) + return 0; + for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) { + if (domain_match(tl->ptr, domain)) + return 1; + } +#ifdef NOPROXY_NETADDR + if (!NOproxy_netaddr) { + return 0; + } + /* + * to check noproxy by network addr + */ + { +#ifndef INET6 + struct hostent *he; + int n; + unsigned char **h_addr_list; + char addr[4 * 16], buf[5]; + + he = gethostbyname(domain); + if (!he) + return (0); + for (h_addr_list = (unsigned char **) he->h_addr_list + ; *h_addr_list; h_addr_list++) { + sprintf(addr, "%d", h_addr_list[0][0]); + for (n = 1; n < he->h_length; n++) { + sprintf(buf, ".%d", h_addr_list[0][n]); + strcat(addr, buf); + } + for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) { + if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0) + return (1); + } + } +#else /* INET6 */ + int error; + struct addrinfo hints; + struct addrinfo *res, *res0; + char addr[4 * 16]; + int *af; + + for (af = ai_family_order_table[DNS_order];; af++) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = *af; + error = getaddrinfo(domain, NULL, &hints, &res0); + if (error) { + if (*af == PF_UNSPEC) { + break; + } + /* try next */ + continue; + } + for (res = res0; res != NULL; res = res->ai_next) { + switch (res->ai_family) { + case AF_INET: + inet_ntop(AF_INET, + &((struct sockaddr_in *) res->ai_addr)->sin_addr, + addr, sizeof(addr)); + break; + case AF_INET6: + inet_ntop(AF_INET6, + &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr, + addr, sizeof(addr)); + break; + default: + /* unknown */ + continue; + } + for (tl = NO_proxy_domains->first; tl != NULL; tl = tl->next) { + if (strncmp(tl->ptr, addr, strlen(tl->ptr)) == 0) { + freeaddrinfo(res0); + return 1; + } + } + } + freeaddrinfo(res0); + if (*af == PF_UNSPEC) { + break; + } + } +#endif /* INET6 */ + } +#endif /* NOPROXY_NETADDR */ + return 0; +} + +char * +filename_extension(char *path, int is_url) +{ + char *last_dot = "", *p = path; + int i; + + if (path == NULL) + return last_dot; + if (*p == '.') + p++; + for (; *p; p++) { + if (*p == '.') { + last_dot = p; + } + else if (is_url && *p == '?') + break; + } + if (*last_dot == '.') { + for (i = 1; last_dot[i] && i < 8; i++) { + if (is_url && !IS_ALNUM(last_dot[i])) + break; + } + return allocStr(last_dot, i); + } + else + return last_dot; +} diff --git a/version.c b/version.c new file mode 100644 index 0000000..0986ca1 --- /dev/null +++ b/version.c @@ -0,0 +1,5 @@ +#define CURRENT_VERSION "w3m/0.2.1" + +#ifndef FM_H +char *version = CURRENT_VERSION; +#endif /* not FM_H */ diff --git a/w3m-doc/README.html b/w3m-doc/README.html new file mode 100644 index 0000000..1caa3cd --- /dev/null +++ b/w3m-doc/README.html @@ -0,0 +1,75 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+
+<HTML>
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>w3m $B%I%-%e%a%s%H@0Hw$NJ}?K(B</TITLE>
+</HEAD>
+
+<BODY>
+
+<DIV ALIGN="center">
+w3m $B%I%-%e%a%s%H@0Hw$NJ}?K(B
+</DIV>
+<DIV ALIGN="right">
+Yoshinobu Sakane February 15, 2001
+</DIV>
+
+<OL>
+<H1><LI>$BJ}?K(B</H1>
+<DIV>
+ <P>
+ w3m$B$K4X$9$k%I%-%e%a%s%HN`$r0J2<$NJ}?K$G@0Hw$7$?$$$H9M$($F$$$^$9!#(B
+ <P>
+ <UL>
+ <LI>$B8=;~E@(B(w3m-0.1.11-pre)$B$G;6J8$7$F$$$k%I%-%e%a%s%H$r0lDj$N%k!<%k$N$b$H$K$^$H$a$k(B
+ <LI>$BF~LgDxEY$N%I%-%e%a%s%H$H?<$_$K$O$a$k(B:-)$B$?$a$N%I%-%e%a%s%H$rMQ0U$9$k(B
+ <LI>HTML$BHG$H%W%l%$%s%F%-%9%HHG$rMQ0U$9$k!#$?$@$7!"%=!<%9$H$J$k%I%-%e%a%s%H$O0l$D(B
+ </UL>
+</DIV>
+<BR>
+
+<H1><LI>$B9|AH$_%I%-%e%a%s%H$N@bL@(B</H1>
+<DIV>
+ <P>
+ <TABLE BORDER>
+ <CAPTION>$B!T%3%s%F%s%D!U(B</CAPTION>
+ <TR>
+ <TD><A HREF="w3mdoc.pl">w3mdoc.pl</A></TD>
+ <TD><A HREF="http://www2u.biglobe.ne.jp/~hsaka/">$B:dK\$5$s(B</A>$B$46`@=$N(Bperl$B%9%/%j%W%H(B</TD>
+ <TR>
+ <TD><A HREF="mkdocs">mkdocs</A></TD>
+ <TD>$B%I%-%e%a%s%H@07AMQ%7%'%k%9%/%j%W%H(B</TD>
+ <TR>
+ <TD>README.html</TD>
+ <TD>$B$3$N%U%!%$%k(B</TD>
+ <TR>
+ <TD>*.html.in</TD>
+ <TD>$B3F>O!?@aKh$N%I%-%e%a%s%H%=!<%9(B</TD>
+ <TR>
+ <TD>*.wd</TD>
+ <TD>$B3F<oDj5A%U%!%$%k(B</TD>
+ </TABLE>
+ <P>
+ w3mdoc.pl$B$N;H$$J}$K$D$$$F$O!":dK\$5$s$,=q$+$l$?(B<A HREF="sample/README">README</A>$B!"5Z$S!"%5%s%W%k%=!<%9$r;2>H$7$F$/$@$5$$!#(B<br>
+</DIV>
+<BR>
+
+<H1><LI>$B%I%-%e%a%s%H@0Hw$N?J$aJ}(B</H1>
+<DIV>
+ <P>
+ <UL>
+ <LI>$BM-;V(B($B0J9_!"(B<B>w3m-doc$B%a%s%P(B</B>$B$H5-$9(B)$B$K$h$kJ,3d:n6H(B
+ <LI>$BJ,3d$NC10L$O(Bw3m-doc$B%a%s%P4V$GD4@0(B
+ <LI>$B:#2s<($99|AH$_$O$"$/$^$G;X?K!#>u67!?ET9g$K$h$j(Bw3m-doc$B%a%s%P4V$GD4@0$7JQ99$9$k(B
+ <LI>$B$^$:$OF|K\8lHG$N%I%-%e%a%s%H$r:n$j!">u67$r8+$F1QLu$9$k(B
+ <LI>$B$"$kDxEY$G$-$?$H$3$m$+$i!"C`<!!"(Bw3m-dev ML$B%a%s%P$N%l%S%e!<$r<u$1$k(B
+ </UL>
+ <P>
+ $B$H!"9M$($F$$$^$9!#(B
+</DIV>
+
+</OL>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/community.html.in b/w3m-doc/community.html.in new file mode 100644 index 0000000..9fbf89f --- /dev/null +++ b/w3m-doc/community.html.in @@ -0,0 +1,45 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>W3M COMMUNITY -w3m$B%3%_%e%K%F%#(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">w3m$B%3%_%e%K%F%#(B</A></H1>
+<DIV>
+<!-- w3m$B%3%_%e%K%F%#$K4X$7$F(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#ML">$B%a!<%j%s%0%j%9%H(B</A>
+ <LI><A HREF="#links">$B4XO"(BWeb</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="ML">$B%a!<%j%s%0%j%9%H(B</A></H2>
+<!-- w3m-dev, w3m-dev-en ML$B$N>R2p(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="links">$B4XO"(BWeb</A><H2>
+<!-- w3m$B$K4X78$7$F$$$k(BWeb -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/configuration.html.in b/w3m-doc/configuration.html.in new file mode 100644 index 0000000..9428a25 --- /dev/null +++ b/w3m-doc/configuration.html.in @@ -0,0 +1,90 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>CONFIGURATION -$B3F<o@_Dj(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B3F<o@_Dj(B</A></H1>
+<DIV>
+<!-- w3m$B$N3F<o@_Dj$N>\:Y$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#option">$B5/F0%*%W%7%g%s(B</A>
+ <LI><A HREF="#environment">$B4D6-JQ?t(B</A>
+ <LI><A HREF="#bookmark">bookmark$B%U%!%$%k(B</A>
+ <LI><A HREF="#option_panel">$B%*%W%7%g%s%Q%M%k(B</A>
+ <UL>
+ <LI><A HREF="#external_viewer">$B30It%S%e!<%"$NJT=8(B</A>
+ </UL>
+ <LI><A HREF="#other_customize">$B$=$NB>%+%9%?%^%$%:(B</A>
+ <UL>
+ <LI><A HREF="#keymap">keymap</A>
+ <LI><A HREF="#menu">menu</A>
+ </UL>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="option">$B5/F0%*%W%7%g%s(B</A></H2>
+<!-- $B%*%W%7%g%s$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="environment">$B4D6-JQ?t(B</A><H2>
+<!-- w3m$B$,;2>H$9$k4D6-JQ?t$K$D$$$F(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="bookmark">bookmark$B%U%!%$%k(B</A><H2>
+<!-- bookmark$B%U%!%$%k$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="option_panel">$B%*%W%7%g%s%Q%M%k(B</A><H2>
+<!-- $B%*%W%7%g%s%Q%M%k$K$D$$$F$N@bL@(B -->
+
+<H3><A NAME="external_viewer">$B30It%S%e!<%"$NJT=8(B</A></H3>
+<!-- $B30It%S%e!<%"$NJT=8$K$D$$$F$N@bL@(B -->
+
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="other_customize">$B$=$NB>%+%9%?%^%$%:(B</A><H2>
+<!-- $B$=$NB>$N%+%9%?%^%$%:9`L\$K$D$$$F$N@bL@(B -->
+
+<H3><A NAME="keymap">keymap</A></H3>
+<!-- keymap$B$N%+%9%?%^%$%:$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H3><A NAME="menu">menu</A></H3>
+<!-- menu$B$N%+%9%?%^%$%:$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/contain.wd b/w3m-doc/contain.wd new file mode 100644 index 0000000..8d48872 --- /dev/null +++ b/w3m-doc/contain.wd @@ -0,0 +1,14 @@ +@define +DOC.index index.html +DOC.prologue prologue.html +DOC.copyright copyright.html +DOC.outline outline.html +DOC.detail detail.html +DOC.install install.html +DOC.operation operation.html +DOC.configuration configuration.html +DOC.function function.html +DOC.FAQ faq.html +DOC.developement developement.html +DOC.community community.html +@end diff --git a/w3m-doc/copyright.html.in b/w3m-doc/copyright.html.in new file mode 100644 index 0000000..1baa369 --- /dev/null +++ b/w3m-doc/copyright.html.in @@ -0,0 +1,45 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>COPYRIGHT -Copyright-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">Copyright</A></H1>
+<DIV>
+<!-- w3m$B$N(BCopyright$B$K4X$7$F(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#copyright">$BCx:n8"!"%i%$%;%s%9(B</A>
+ <LI><A HREF="#thanks">$B<U<-(B</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="copyright">$BCx:n8"!"%i%$%;%s%9(B</A></H2>
+<!-- w3m$B$NCx:n8"!"%i%$%;%s%9$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="thanks">$B<U<-(B</A><H2>
+<!-- $B<U<-(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/define.wd b/w3m-doc/define.wd new file mode 100644 index 0000000..5967679 --- /dev/null +++ b/w3m-doc/define.wd @@ -0,0 +1,4 @@ +@define +W3M.version 0.1.11-pre+kokb24+test1 +W3M.author Akinori ITO +@end diff --git a/w3m-doc/detail.html.in b/w3m-doc/detail.html.in new file mode 100644 index 0000000..9e14c2c --- /dev/null +++ b/w3m-doc/detail.html.in @@ -0,0 +1,32 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>DETAIL -$B>\:YJT(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B>\:YJT(B</A></H1>
+<DIV>
+<!-- w3m$B$N3F<o5!G=$K$D$$$F$N>\:Y$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="@DOC.install@">$B%$%s%9%H!<%k$N>\:Y(B</A>
+ <LI><A HREF="@DOC.operation@">w3m$BA`:nK!(B</A> <!-- $B%-!<A`:n@bL@(B -->
+ <LI><A HREF="@DOC.configuration@">$B3F<o@_Dj(B</A>
+ <LI><A HREF="@DOC.function@">$B5!G=>\:Y(B</A>
+ <LI><A HREF="@DOC.FAQ@">FAQ</A>
+ <LI><A HREF="@DOC.developement@">$B3+H/%I%-%e%a%s%H(B</A>
+ <LI><A HREF="@DOC.community@">w3m $B%3%_%e%K%F%#(B</A>
+ <LI><A HREF="@DOC.copyright@">Copiright</A>
+ </UL>
+<HR>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/developement.html.in b/w3m-doc/developement.html.in new file mode 100644 index 0000000..0da2b38 --- /dev/null +++ b/w3m-doc/developement.html.in @@ -0,0 +1,77 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>DEVELOPEMENT -$B3+H/%I%-%e%a%s%H(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">w3m$B3+H/%I%-%e%a%s%H(B</A></H1>
+<DIV>
+<!-- w3m$B$N3+H/$K4X$7$F$$$m$$$m$H5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#story">w3m$B$N3+H/$K$D$$$F(B</A>
+ <LI><A HREF="#known_bugs">$B8=:_H=L@$7$F$$$k%P%0(B</A>
+ <LI><A HREF="#ToDo">ToDo</A>
+ <LI><A HREF="#history">$BMzNr(B</A>
+ <LI><A HREF="#policy">$B3+H/%]%j%7!<(B</A>
+ <LI><A HREF="#tips">$B3+H/<T8~$1(BTips</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="story">w3m$B$N3+H/$K$D$$$F(B</A></H2>
+<!-- doc-jp/STORY.html$B$+$i0zMQ(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="known_bugs">$B8=:_H=L@$7$F$$$k%P%0(B</A><H2>
+<!-- $B8=:_H=L@$7$F$$$k%P%0(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="ToDo">ToDo</A><H2>
+<!-- $B$d$i$M$P$J$i$s$3$H!?$d$m$&$+$J$!$J$3$H(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="history">$BMzNr(B</A><H2>
+<!-- w3m$B3+H/MzNr(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="policy">$B3+H/%]%j%7!<(B</A><H2>
+<!-- w3m$B$NL\;X$9$3$H!?3+H/>e$N$3$@$o$j(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="tips">$B3+H/<T8~$1(BTips</A><H2>
+<!-- w3m$B$r3+H/$9$k$&$($G$N>.5;(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/faq.html.in b/w3m-doc/faq.html.in new file mode 100644 index 0000000..b85a52f --- /dev/null +++ b/w3m-doc/faq.html.in @@ -0,0 +1,45 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>FAQ -FAQ-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">FAQ</A></H1>
+<DIV>
+<!-- w3m$B$K4X$7$FNI$/J9$+$l$k(B($B$G$"$m$&(B)$B<ALd$H$=$NEz$(!"$*$h$S!"(BTips$B$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#faq">$B$h$/J9$+$l$k<ALd$H$=$NEz$((B</A>
+ <LI><A HREF="#tips">Tips</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="faq">$B$h$/J9$+$l$k<ALd$H$=$NEz$((B</A></H2>
+<!-- w3m$B$K4X$7$FNI$/J9$+$l$k<ALd$H$=$NEz$($K$D$$$F(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="tips">Tips</A><H2>
+<!-- Tips($B>.5;(B) -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/function.html.in b/w3m-doc/function.html.in new file mode 100644 index 0000000..ec5eb0f --- /dev/null +++ b/w3m-doc/function.html.in @@ -0,0 +1,71 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>FUNCTION -$B5!G=>\:Y(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B5!G=>\:Y(B</A></H1>
+<DIV>
+<!-- w3m$B$N3F5!G=$K$D$$$F$N>\:Y@bL@$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#url">$BBP1~$7$F$$$k(BURL</A>
+ <LI><A HREF="#html">$BBP1~$7$F$$$k(BHTML</A>
+ <LI><A HREF="#ssl">SSL</A>
+ <LI><A HREF="#cookie">cookie</A>
+ <LI><A HREF="#local_CGI">$B%m!<%+%k(BCGI$B5!G=(B</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="url">$BBP1~$7$F$$$k(BURL</A></H2>
+<!-- w3m$B$,2r<a2DG=$J(BURL$B$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="html">$BBP1~$7$F$$$k(BHTML</A><H2>
+<!-- w3m$B$,BP1~$7$F$$$k(BHTML$B$K$D$$$F(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="ssl">SSL</A><H2>
+<!-- SSL$B$K$D$$$F$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="cookie">cookie</A><H2>
+<!-- cookie$B$K$D$$$F$N@bL@(B -->
+
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="local_CGI">$B%m!<%+%k(BCGI$B5!G=(B</A><H2>
+<!-- $B%m!<%+%k(BCGI$B5!G=$K$D$$$F$N@bL@(B -->
+
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/index.html.in b/w3m-doc/index.html.in new file mode 100644 index 0000000..0e0e625 --- /dev/null +++ b/w3m-doc/index.html.in @@ -0,0 +1,113 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>THE DOCUMENTS FOR W3M</TITLE>
+</HEAD>
+
+<BODY>
+
+<!-- $B$3$N$"$?$j$K(B w3m$B$N%m%4(B($B$C$F$J$$$+!)(B) $B$H$+(B -->
+<!-- w3m: WWW wo Miru Tool version @W3M.version@ $B$H$+(B -->
+<!-- (C) Copyright by @W3M.author@ March 23, 1999 $B$H$+(B -->
+<!-- $B$rF~$l$F$*$/$Y$-$+$J!)(B -->
+
+<H1><A NAME="index">$B:w0z(B</A></H1>
+<OL>
+ <LI><A HREF="@DOC.prologue@" NAME="prologue">$B$O$8$a$K(B</A> <!-- README $B$N!H(B1. $B$O$8$a$K!I$NItJ,(B -->
+ <UL>
+ <LI><A HREF="@DOC.prologue@#summary">w3m$B$H$O(B</A>
+ <LI><A HREF="@DOC.prologue@#glossary">$BMQ8l$N@bL@(B</A>
+ <LI><A HREF="@DOC.prologue@#disclaimer">$BCm0U;v9`(B</A>
+ <LI><A HREF="@DOC.copyright@">$BCx:n8"!"%i%$%;%s%9(B</A>
+ <LI><A HREF="@DOC.prologue@#confirmation">$BF0:n3NG'(BOS</A>
+ </UL>
+ <LI><A HREF="@DOC.outline@" NAME="outline">$B35MWJT(B</A>
+ <UL>
+ <LI><A HREF="@DOC.outline@#install">$B%$%s%9%H!<%k$N35MW(B</A> <!-- $B35MW$N$_!%>\:Y$OJL$N>O$G(B -->
+ <LI><A HREF="@DOC.outline@#operation">$BA`:nJ}K!$N35MW(B</A> <!-- $B35MW$N$_!%>\:Y$OJL$N>O$G(B -->
+ <UL>
+ <LI><A HREF="@DOC.outline@#display">$BI=<(FbMF$N@bL@(B</A>
+ <LI><A HREF="@DOC.outline@#basic">$B4pK\E*$J;H$$J}(B</A>
+ <LI><A HREF="@DOC.outline@#help">$B%X%k%W$N;2>H(B</A>
+ </UL>
+ </UL>
+ <LI><A HREF="@DOC.detail@" NAME="detail">$B>\:YJT(B</A>
+ <UL>
+ <LI><A HREF="@DOC.install@" NAME="install">$B%$%s%9%H!<%k$N>\:Y(B</A>
+ <UL>
+ <LI><A HREF="@DOC.install@#get_w3m">w3m$BF~<jJ}K!(B</A>
+ <UL>
+ <LI><A HREF="@DOC.install@#w3m_home">w3m home</A>
+ <LI><A HREF="@DOC.install@#BBS">aito$BO"MmD"(B</A>
+ </UL>
+ <LI><A HREF="@DOC.install@#require">$BI,MW$JJ*(B</A>
+ <LI><A HREF="@DOC.install@#configure">configure$B$K$D$$$F(B</A>
+ <LI><A HREF="@DOC.install@#config_h">config.h$B$K$D$$$F(B</A>
+ <LI><A HREF="@DOC.install@#make">make$B$K$D$$$F(B</A>
+ <LI><A HREF="@DOC.install@#tips">$BB>$N(BOS$B$G%$%s%9%H!<%k$9$k:]$N(BTips</A>
+ </UL>
+ <LI><A HREF="@DOC.operation@" NAME="operation">w3m$BA`:nK!(B</A> <!-- $B%-!<A`:n@bL@(B -->
+ <UL>
+ <LI><A HREF="@DOC.operation@#buffer">$B%P%C%U%!A`:n(B</A>
+ <LI><A HREF="@DOC.operation@#mouse">$B%^%&%9A`:n(B</A>
+ <LI><A HREF="@DOC.operation@#line_edit">$B:G2<9TF~NO(B</A>
+ <LI><A HREF="@DOC.operation@#menu">$B%a%K%e!<A`:n(B</A>
+ </UL>
+ <LI><A HREF="@DOC.configuration@" NAME="configuration">$B3F<o@_Dj(B</A>
+ <UL>
+ <LI><A HREF="@DOC.configuration@#option">$B5/F0%*%W%7%g%s(B</A>
+ <LI><A HREF="@DOC.configuration@#environment">$B4D6-JQ?t(B</A>
+ <LI><A HREF="@DOC.configuration@#bookmark">bookmark$B%U%!%$%k(B</A>
+ <LI><A HREF="@DOC.configuration@#option_panel">$B%*%W%7%g%s%Q%M%k(B</A>
+ <UL>
+ <LI><A HREF="@DOC.configuration@#external_viewer">$B30It%S%e!<%"$NJT=8(B</A>
+ </UL>
+ <LI><A HREF="@DOC.configuration@#other_customize">$B$=$NB>%+%9%?%^%$%:(B</A>
+ <UL>
+ <LI><A HREF="@DOC.configuration@#keymap">keymap</A>
+ <LI><A HREF="@DOC.configuration@#menu">menu</A>
+ </UL>
+ </UL>
+ <LI><A HREF="@DOC.function@" NAME="function">$B5!G=>\:Y(B</A>
+ <UL>
+ <LI><A HREF="@DOC.function@#url">$BBP1~$7$F$$$k(BURL</A>
+ <LI><A HREF="@DOC.function@#html">$BBP1~$7$F$$$k(BHTML</A>
+ <LI><A HREF="@DOC.function@#ssl">SSL</A>
+ <LI><A HREF="@DOC.function@#cookie">cookie</A>
+ <LI><A HREF="@DOC.function@#local_CGI">$B%m!<%+%k(BCGI$B5!G=(B</A>
+ </UL>
+ <LI><A HREF="@DOC.FAQ@" NAME="faq">FAQ</A>
+ <UL>
+ <LI><A HREF="@DOC.FAQ@#faq">$B$h$/J9$+$l$k<ALd$H$=$NEz$((B</A>
+ <LI><A HREF="@DOC.FAQ@#tips">Tips</A>
+ </UL>
+ <LI><A HREF="@DOC.developement@" NAME="developement">$B3+H/%I%-%e%a%s%H(B</A>
+ <UL>
+ <LI><A HREF="@DOC.developement@#story">w3m$B$N3+H/$K$D$$$F(B</A>
+ <LI><A HREF="@DOC.developement@#known_bugs">$B8=:_H=L@$7$F$$$k%P%0(B</A>
+ <LI><A HREF="@DOC.developement@#ToDo">ToDo</A>
+ <LI><A HREF="@DOC.developement@#history">$BMzNr(B</A>
+ <LI><A HREF="@DOC.developement@#policy">$B3+H/%]%j%7!<(B</A>
+ <LI><A HREF="@DOC.developement@#tips">$B3+H/<T8~$1(BTips</A>
+ </UL>
+ <LI><A HREF="@DOC.community@" NAME="community">w3m$B%3%_%e%K%F%#(B</A>
+ <UL>
+ <LI><A HREF="@DOC.community@#ML">$B%a!<%j%s%0%j%9%H(B</A>
+ <LI><A HREF="@DOC.community@#links">$B4XO"(BWeb</A>
+ </UL>
+ <LI><A HREF="@DOC.copyright@" NAME="copyright">Copyright</A>
+ <UL>
+ <LI><A HREF="@DOC.copyright@#copyright">$BCx:n8"!"%i%$%;%s%9(B</A>
+ <LI><A HREF="@DOC.copyright@#thanks">$B<U<-(B</A>
+ </UL>
+ </UL>
+</OL>
+
+</BODY>
+
+</HTML>
diff --git a/w3m-doc/install.html.in b/w3m-doc/install.html.in new file mode 100644 index 0000000..bc93476 --- /dev/null +++ b/w3m-doc/install.html.in @@ -0,0 +1,88 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>INSTALL -$B%$%s%9%H!<%k$N>\:Y(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B%$%s%9%H!<%k$N>\:Y(B</A></H1>
+<DIV>
+<!-- w3m$B$N%$%s%9%H!<%k$N>\:Y$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#get_w3m">w3m$BF~<jJ}K!(B</A>
+ <UL>
+ <LI><A HREF="#w3m_home">w3m home</A>
+ <LI><A HREF="#BBS">aito$BO"MmD"(B</A>
+ </UL>
+ <LI><A HREF="#require">$BI,MW$JJ*(B</A>
+ <LI><A HREF="#configure">configure$B$K$D$$$F(B</A>
+ <LI><A HREF="#config_h">config.h$B$K$D$$$F(B</A>
+ <LI><A HREF="#make">make$B$K$D$$$F(B</A>
+ <LI><A HREF="#tips">$BB>$N(BOS$B$G%$%s%9%H!<%k$9$k:]$N(BTips</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="get_w3m">w3m$BF~<jJ}K!(B</A></H2>
+<!-- w3m$BF~<jJ}K!(B -->
+
+<H3><A NAME="w3m_home">w3m home</A><H3>
+<!-- w3m HOME page$B$K$D$$$F(B -->
+
+<H3><A NAME="BBS">aito$BO"MmD"(B</A><H3>
+<!-- aito$BO"MmD"!"$=$NB>7G<(HD(B -->
+
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="require">$BI,MW$JJ*(B</A></H2>
+<!-- w3m$B$r(Bmake$B$9$k$&$($GI,MW$JJ*!?$"$l$PNI$$J*(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="configure">configure$B$K$D$$$F(B</A></H2>
+<!-- configure$B<B9T;~$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="config_h">config.h$B$K$D$$$F(B</A></H2>
+<!-- config.h$B$G(Bdefine$B$9$k$b$NEy$K$D$$$F(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="make">make$B$K$D$$$F(B</A></H2>
+<!-- make$B;~$N@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="tips">$BB>$N(BOS$B$G%$%s%9%H!<%k$9$k:]$N(BTips</A><H2>
+<!-- $B3F<o(BOS$B>e$G(Bw3m$B$r%$%s%9%H!<%k$9$k:]$N(BTips -->
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/mkdocs b/w3m-doc/mkdocs new file mode 100755 index 0000000..72c7b53 --- /dev/null +++ b/w3m-doc/mkdocs @@ -0,0 +1,32 @@ +#!/bin/sh + +W3MDOC="./w3mdoc.pl" +W3M="$HOME/bin/pre_w3m" +#W3M="w3m" + +NKF="/usr/local/bin/nkf" +SED="/usr/bin/sed" +TR="/usr/bin/tr" + +HTML_JP_DIR="html-jp" +DOC_JP_DIR="doc-jp" +#HTML_DIR="html" +#DOC_DIR="doc" + +if [ ! -d ${HTML_JP_DIR} ]; then + mkdir ${HTML_JP_DIR} +fi +if [ ! -d ${DOC_JP_DIR} ]; then + mkdir ${DOC_JP_DIR} +fi + +for SRC in *.in +do + HTML=`echo ${SRC} | ${SED} 's/\.in$//p'` + DOC=`echo ${HTML} | ${SED} 's/\.html$//p' | ${TR} '[a-z]' '[A-Z]'` + echo "converting ${SRC} to ${HTML_JP_DIR}/${HTML} ... \c" + ${NKF} -e ${SRC} | ${SED} -e 's/��/��/gp' -e 's/��/��/gp' | ${NKF} -j | ${W3MDOC} > ${HTML_JP_DIR}/${HTML} + echo "done.\nconverting ${HTML} to ${DOC_JP_DIR}/${DOC} ... \c" + ${W3M} -dump -e ${HTML_JP_DIR}/${HTML} > ${DOC_JP_DIR}/${DOC} + echo "done." +done diff --git a/w3m-doc/operation.html.in b/w3m-doc/operation.html.in new file mode 100644 index 0000000..2534e63 --- /dev/null +++ b/w3m-doc/operation.html.in @@ -0,0 +1,50 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>OPERATION -w3m$BA`:nK!(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">w3m$BA`:nK!(B</A></H1>
+<DIV>
+<!-- w3m$B$rA`:n$9$k>e$G$N>\:Y$r5-$9(B -->
+</DIV>
+ <UL>
+ <LI><A HREF="#buffer">$B%P%C%U%!A`:n(B</A>
+ <LI><A HREF="#mouse">$B%^%&%9A`:n(B</A>
+ <LI><A HREF="#line_edit">$B:G2<9TF~NO(B</A>
+ <LI><A HREF="#menu">$B%a%K%e!<(B</A>
+ </UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="buffer">$B%P%C%U%!A`:n(B</A></H2>
+<!-- $B%P%C%U%!A`:n;~$N@bL@(B -->
+
+<H3><A NAME="mouse">$B%^%&%9A`:n(B</A><H3>
+<!-- $B%^%&%9A`:n;~$N@bL@(B -->
+
+<H3><A NAME="line_edit">$B:G2<9TF~NO(B</A><H3>
+<!-- $B:G2<9TF~NO;~$NA`:n$K$D$$$F$N@bL@(B -->
+
+<H3><A NAME="menu">$B%a%K%e!<A`:n(B</A><H3>
+<!-- $B%a%K%e!<A`:n;~$N@bL@(B -->
+
+<DIV>
+<A HREF="#index">$B$3$N%Z!<%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/outline.html.in b/w3m-doc/outline.html.in new file mode 100644 index 0000000..aa6b9a4 --- /dev/null +++ b/w3m-doc/outline.html.in @@ -0,0 +1,61 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>OUTLINE -$B35MWJT(B-</TITLE>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B35MWJT(B</A></H1>
+<UL>
+ <LI><A HREF="#install">$B%$%s%9%H!<%k$N35MW(B</A>
+ <LI><A HREF="#operation">$BA`:nJ}K!$N35MW(B</A>
+ <UL>
+ <LI><A HREF="#display">$BI=<(FbMF$N@bL@(B</A>
+ <LI><A HREF="#basic">$B4pK\E*$J;H$$J}(B</A>
+ <LI><A HREF="#help">$B%X%k%W$N;2>H(B</A>
+ </UL>
+</UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+
+<HR>
+
+<H2><A NAME="install">$B%$%s%9%H!<%k$N35MW(B</A><H2>
+<!-- $B%$%s%9%H!<%k$N35MW(B -->
+<DIV>
+<A HREF="index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="operation">$BA`:nJ}K!$N35MW(B</A><H2>
+<!-- $BA`:nJ}K!$N35MW(B -->
+
+<H3><A NAME="display">$BI=<(FbMF$N@bL@(B</A></H3>
+<!-- $BI=<(FbMF$N@bL@(B -->
+
+<H3><A NAME="basic">$B4pK\E*$J;H$$J}(B</A></H3>
+<!-- $B4pK\E*$J;H$$J}(B -->
+
+<H3><A NAME="help">$B%X%k%W$N;2>H(B</A></H3>
+<!-- $B%X%k%W$N;2>H(B -->
+
+<DIV>
+<A HREF="index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+
+</BODY>
+</HTML>
+
diff --git a/w3m-doc/prologue.html.in b/w3m-doc/prologue.html.in new file mode 100644 index 0000000..8af21a8 --- /dev/null +++ b/w3m-doc/prologue.html.in @@ -0,0 +1,69 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
+<HTML>
+
+@include define.wd
+@include contain.wd
+
+<HEAD>
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-2022-jp">
+<TITLE>PROLOGUE -$B$O$8$a$K(B-</title>
+</HEAD>
+
+<BODY>
+
+<H1><A NAME="index">$B$O$8$a$K(B</A></H1>
+
+<UL>
+ <LI><A HREF="#summary">w3m$B$H$O(B</A>
+ <LI><A HREF="#glossary">$BMQ8l$N@bL@(B</A>
+ <LI><A HREF="#disclaimer">$BCm0U;v9`(B</A>
+ <LI><A HREF="@DOC.copyright@">$BCx:n8"!"%i%$%;%s%9(B</A>
+ <LI><A HREF="#confirmation">$BF0:n3NG'(BOS</A>
+</UL>
+
+<DIV>
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="summary">w3m$B$H$O(B</A></H2>
+<!-- w3m$B$H$O(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="glossary">$BMQ8l@bL@(B</A></H2>
+<!-- $BMQ8l@bL@(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="disclaimer">$BCm0U;v9`(B</A></H2>
+<!-- $BCm0U;v9`(B -->
+<DIV>
+<A HREF="#index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2>$BCx:n8"!"%i%$%;%s%9(B</H2>
+<DIV>
+ <A HREF="@DOC.copyright@">$BCx:n8"!"%i%$%;%s%9(B</A>$B$r;2>H$/$@$5$$!#(B
+</DIV>
+<DIV>
+<A HREF="#index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<H2><A NAME="confirmation">$BF0:n3NG'(BOS</A></H2>
+<!-- $BF0:n3NG'(BOS -->
+<DIV>
+<A HREF="#index">$B$3$N%Z%$%8$N@hF,$KLa$k(B</A>
+</DIV>
+<HR>
+
+<A HREF="@DOC.index@">$B%H%C%W%Z%$%8$KLa$k(B</A>
+
+</BODY>
+</HTML>
diff --git a/w3m-doc/sample/README b/w3m-doc/sample/README new file mode 100644 index 0000000..7bb9dab --- /dev/null +++ b/w3m-doc/sample/README @@ -0,0 +1,41 @@ + +�Ȥ��� + perl w3mdoc.pl sample.wd > sample.html + +Ÿ��������� + +* @xxx.yyy@ + + @define �� @end ���������줿�ͤ�Ÿ������롣 + +* @YYY(xxx)@ + + @code �� @end ���������줿�ؿ���ƤӽФ���Ÿ������롣 + + ��) + URL(xxx) xxx.url + LINK(xxx) <A HREF="xxx.url">xxx.title</A> + MAILTO(xxx) <A HREF="mailto:xxx.address">xxx.name</A> + +���ޥ�ɰ��� + +* ��� + @define + xxx.yyy zzz + @end + + xxx.yyy �� zzz ��������롣xxx �ϰʲ��δؿ��ΰ����ˤ�ʤ롣 + ��� define.wd �ȡ� + +* ������(�ؿ����) + @code + ������ + @end + + w3mdoc ��������Ƥ������Υ����ɤ�ľ�ܽ��Ȥꤢ���� perl5�� + ��� html.wd �ȡ� + +* ���롼�� + @include file + + �ե������ include ���롣 diff --git a/w3m-doc/sample/define.wd b/w3m-doc/sample/define.wd new file mode 100644 index 0000000..f588f4c --- /dev/null +++ b/w3m-doc/sample/define.wd @@ -0,0 +1,4 @@ +@define +hsaka.url http://www2u.biglobe.ne.jp/~hsaka/ +hsaka.title ���ܹ�§�Υۡ���ڡ��� +@end diff --git a/w3m-doc/sample/html.wd b/w3m-doc/sample/html.wd new file mode 100644 index 0000000..1b1d7f2 --- /dev/null +++ b/w3m-doc/sample/html.wd @@ -0,0 +1,18 @@ +@code +sub URL { + $_[0]->CHECK(qw(url)); + $_[0]->{url}; +} +sub LINK { + $_[0]->CHECK(qw(url title)); + "<A HREF=\"$_[0]->{url}\">$_[0]->{title}</A>"; +} +sub LINK_SEC { + $_[0]->CHECK(qw(url section title)); + "<A HREF=\"$_[0]->{url}\">$_[0]->{section} $_[0]->{title}</A>"; +} +sub MAILTO { + $_[0]->CHECK(qw(address name)); + "<A HREF=\"mailto:$_[0]->{address}\">$_[0]->{name}</A>"; +} +@end diff --git a/w3m-doc/sample/keymap.cgi b/w3m-doc/sample/keymap.cgi new file mode 100755 index 0000000..f68f5fb --- /dev/null +++ b/w3m-doc/sample/keymap.cgi @@ -0,0 +1,268 @@ +#!/usr/local/bin/perl + +$QUERY = $ENV{'QUERY_STRING'}; +$KEYMAP = "$ENV{'HOME'}/.w3m/keymap"; + +if ($QUERY) { + # &write_keymap($QUERY); + print <<EOF; +Content-Type: text/html +w3m-control: DELETE_PREVBUF +w3m-control: BACK + +EOF + exit; +} +&init_func(); +@key = (); +@func = (); +@data = (); +open(KEYMAP, $KEYMAP); +while (<KEYMAP>) { + s/^keymap\s+// || next; + (($k, $_) = &getQWord($_)) || next; + (($f, $_) = &getWord($_)) || next; + $FUNC_EXIST{$f} || next; + ($d, $_) = &getQWord($_); + push(@key, $k); + push(@func, $f); + push(@data, $d); +} +close(KEYMAP); + +$N = @key; + +print <<EOF; +Content-Type: text/html + +<head><title>Keymap Setting</title></head> +<h1>Keymap Setting</h1> +<form action="file:///\$LIB/keymap.cgi"> +<table> +<tr><td> Key<td> Command<td> Argument +<tr><td><input name=k_$N size=6> +<td><select name=f_$N> +EOF +&print_func(); +print <<EOF; +</select> +<td><input name=d_$N> +<td><input type=submit name=ok value=Ok> +<tr><td colspan=4><hr> +EOF +$i = 0; +while(@key) { + $k = &Q(shift @key); + $f = shift @func; + $d = &Q(shift @data); + print <<EOF; +<tr><td><input type=hidden name=k_$i value=\"$k\"> $k +<td><select name=f_$i> +EOF + &print_func($f); + print <<EOF; +</select> +<td><input name=d_$i value=\"$d\"> +<td><input type=checkbox name=del_$i>Delete +EOF + $i++; +} +print <<EOF; +</table> +</form> +EOF + +sub write_keymap { + local($query) = @_; + @key = (); + @func = (); + @data = (); + + for $q (split('&', $query)) { + ($_, $d) = split('=', $q); + if (s/^k_//) { + $key[$_] = $d; + } elsif (s/^f_//) { + $func[$_] = $d; + } elsif (s/^d_//) { + $data[$_] = $d; + } elsif (s/^del_//) { + $del[$_] = 1; + } + } + open(KEYMAP, "> ${KEYMAP}") || next; + while(@key) { + $k = &UQ(shift @key); + $f = shift @func; + $d = &UQ(shift @data); + ($f =~ /^\w/) || next; + (shift @del) && next; + print KEYMAP "keymap\t$k\t$f"; + if ($d ne '') { + if ($d =~ /[\"\'\\\s]/) { + $d =~ s/([\"\\])/\\$1/g; + print KEYMAP "\t\t\"$d\""; + } else { + $d =~ s/([\"\\])/\\$1/g; + print KEYMAP "\t\t$d"; + } + } + print KEYMAP "\n"; + } + close(KEYMAP); +} + +sub UQ { + local($_) = @_; + s/\+/ /g; + s/%([\da-f][\da-f])/pack('c', hex($1))/egi; + return $_; +} + +sub Q { + local($_) = @_; + s/\&/\&/g; + s/\</\</g; + s/\>/\>/g; + s/\"/\"/g; + return $_; +} + +sub getQWord { + local($_) = @_; + local($x) = ''; + s/^\s+//; + while($_ ne '') { + if (s/^\'(([^\'\\]|\\.)*)\'// || + s/^\"(([^\"\\]|\\.)*)\"// || + s/^([^\'\"\\\s]+)// || s/^\\(.)//) { + $x .= $1; + } else { + last; + } + } + return ($x, $_); +} + +sub getWord { + local($_) = @_; + s/^\s+//; + s/^(\S+)// || return (); + return ($1, $_); +} + +sub print_func { + local($f) = @_; + for(@FUNC_LIST) { + if ($f eq $_) { + print "<option selected>$_\n"; + } else { + print "<option>$_\n"; + } + } +} + +sub init_func { + @FUNC_LIST = (); + %FUNC_EXIST = (); + while(<DATA>) { + chop; + push(@FUNC_LIST, $_); + $FUNC_EXIST{$_} = 1; + } +} + +__END__ +- - - - - - - +ABORT +ADD_BOOKMARK +BACK +BEGIN +BOOKMARK +CENTER_H +CENTER_V +COOKIE +DELETE_PREVBUF +DICT_WORD +DICT_WORD_AT +DOWN +DOWNLOAD +EDIT +EDIT_SCREEN +END +ESCBMAP +ESCMAP +EXEC_SHELL +EXIT +EXTERN +EXTERN_LINK +FRAME +GOTO +GOTO_LINE +GOTO_LINK +HELP +HISTORY +INFO +INIT_MAILCAP +INTERRUPT +LEFT +LINE_BEGIN +LINE_END +LINE_INFO +LINK_BEGIN +LINK_END +LOAD +MAIN_MENU +MARK +MARK_MID +MARK_URL +MENU +MOUSE +MOUSE_TOGGLE +MOVE_DOWN +MOVE_LEFT +MOVE_RIGHT +MOVE_UP +NEXT_LINK +NEXT_MARK +NEXT_PAGE +NEXT_WORD +NOTHING +NULL +OPTIONS +PCMAP +PEEK +PEEK_LINK +PIPE_SHELL +PREV_LINK +PREV_MARK +PREV_PAGE +PREV_WORD +PRINT +QUIT +READ_SHELL +REDRAW +REG_MARK +RELOAD +RIGHT +SAVE +SAVE_IMAGE +SAVE_LINK +SAVE_SCREEN +SEARCH +SEARCH_BACK +SEARCH_FORE +SEARCH_NEXT +SEARCH_PREV +SELECT +SHELL +SHIFT_LEFT +SHIFT_RIGHT +SOURCE +SUSPEND +UP +VIEW +VIEW_BOOKMARK +VIEW_IMAGE +WHEREIS +WRAP_TOGGLE diff --git a/w3m-doc/sample/s.wd b/w3m-doc/sample/s.wd new file mode 100644 index 0000000..02ae4ce --- /dev/null +++ b/w3m-doc/sample/s.wd @@ -0,0 +1,8 @@ +@include html.wd +@include define.wd + +����ϥ���ץ�Ǥ��� +<P> +\@hsaka.url\@ = @hsaka.url@ +<BR> +\@LINK(hsaka)\@ = @LINK(hsaka)@ diff --git a/w3m-doc/sample/sample.html b/w3m-doc/sample/sample.html new file mode 100644 index 0000000..b58b0c7 --- /dev/null +++ b/w3m-doc/sample/sample.html @@ -0,0 +1,6 @@ + +����ϥ���ץ�Ǥ��� +<P> +@hsaka.url@ = http://www2u.biglobe.ne.jp/~hsaka/ +<BR> +@LINK(hsaka)@ = <A HREF="http://www2u.biglobe.ne.jp/~hsaka/">���ܹ�§�Υۡ���ڡ���</A> diff --git a/w3m-doc/sample/sample.wd b/w3m-doc/sample/sample.wd new file mode 100644 index 0000000..0edfd0c --- /dev/null +++ b/w3m-doc/sample/sample.wd @@ -0,0 +1,11 @@ +@include html.wd +@define +hsaka.url http://www2u.biglobe.ne.jp/~hsaka/ +hsaka.title ���ܹ�§�Υۡ���ڡ��� +@end + +����ϥ���ץ�Ǥ��� +<P> +\@hsaka.url\@ = @hsaka.url@ +<BR> +\@LINK(hsaka)\@ = @LINK(hsaka)@ diff --git a/w3m-doc/sample/w3mdoc.pl b/w3m-doc/sample/w3mdoc.pl new file mode 100755 index 0000000..6097926 --- /dev/null +++ b/w3m-doc/sample/w3mdoc.pl @@ -0,0 +1,102 @@ +#!/usr/local/bin/perl + +package w3mdoc; + +sub CHECK { + my($a, @b) = @_; + for(@b) { + defined($a->{$_}) || die("\"$a->{id}.$_\" is not defined.\n"); + } +} + +sub DEF { + my($a, $b, $c) = @_; + + if (! defined($data->{$a})) { + $data->{$a} = bless { id => $a }; + } + $data->{$a}{$b} = $c; +} + +sub SUB { + local($_) = @_; + my($a, $b); + + if (/^\@(\w+)\.(\w+)\@$/) { + ($a, $b) = ($1, $2); + defined($data->{$a}) || die("\"$a.$b\" is not defined.\n"); + $data->{$a}->CHECK($b); + return $data->{$a}{$b}; + } + if (/^\@(\w+)\((\w+)\)\@$/) { + ($a, $b) = ($1, $2); + defined(&{$a}) || die("\"$a()\" is not defined.\n"); + defined($data->{$b}) || die("\"$a($b)\" is not defined.\n"); + return $data->{$b}->$a(); + } + return '@'; +} + +package main; + +@ARGV || unshift(@ARGV, "-"); +while(@ARGV) { + $file = shift @ARGV; + &make_doc($file); +} + +sub make_doc { + my($file) = @_; + my($in_def, $in_code, $code, $a, $b); + local(*F); + local($_); + + open(F, $file) || die("$file: $!\n"); + $in_def = 0; + $in_code = 0; + while(<F>) { + if ($in_def) { + if (/^\@end/) { + $in_def = 0; + next; + } + s/^\s+//; + s/^(\w+)\.(\w+)// || next; + ($a, $b) = ($1, $2); + s/^\s+//; + s/\s+$//; + &w3mdoc::DEF($a, $b, $_); + next; + } + if ($in_code) { + if (/^\@end/) { + eval "package w3mdoc; $code"; + $in_code = 0; + next; + } + $code .= $_; + next; + } + if (/^\@define/) { + $in_def = 1; + next; + } + if (/^\@code/) { + $in_code = 1; + $code = ""; + next; + } + if (s/^\@include\s+//) { + s/\s+$//; + &make_doc($_); + next; + } + if (/^\@/) { + die("unknown command: $_"); + } + s/(\\@|\@(\w+(\.\w+|\(\w+\)))\@)/&w3mdoc::SUB($1)/eg; + print; + } + close(F); +} + diff --git a/w3m-doc/w3mdoc.pl b/w3m-doc/w3mdoc.pl new file mode 100755 index 0000000..6097926 --- /dev/null +++ b/w3m-doc/w3mdoc.pl @@ -0,0 +1,102 @@ +#!/usr/local/bin/perl + +package w3mdoc; + +sub CHECK { + my($a, @b) = @_; + for(@b) { + defined($a->{$_}) || die("\"$a->{id}.$_\" is not defined.\n"); + } +} + +sub DEF { + my($a, $b, $c) = @_; + + if (! defined($data->{$a})) { + $data->{$a} = bless { id => $a }; + } + $data->{$a}{$b} = $c; +} + +sub SUB { + local($_) = @_; + my($a, $b); + + if (/^\@(\w+)\.(\w+)\@$/) { + ($a, $b) = ($1, $2); + defined($data->{$a}) || die("\"$a.$b\" is not defined.\n"); + $data->{$a}->CHECK($b); + return $data->{$a}{$b}; + } + if (/^\@(\w+)\((\w+)\)\@$/) { + ($a, $b) = ($1, $2); + defined(&{$a}) || die("\"$a()\" is not defined.\n"); + defined($data->{$b}) || die("\"$a($b)\" is not defined.\n"); + return $data->{$b}->$a(); + } + return '@'; +} + +package main; + +@ARGV || unshift(@ARGV, "-"); +while(@ARGV) { + $file = shift @ARGV; + &make_doc($file); +} + +sub make_doc { + my($file) = @_; + my($in_def, $in_code, $code, $a, $b); + local(*F); + local($_); + + open(F, $file) || die("$file: $!\n"); + $in_def = 0; + $in_code = 0; + while(<F>) { + if ($in_def) { + if (/^\@end/) { + $in_def = 0; + next; + } + s/^\s+//; + s/^(\w+)\.(\w+)// || next; + ($a, $b) = ($1, $2); + s/^\s+//; + s/\s+$//; + &w3mdoc::DEF($a, $b, $_); + next; + } + if ($in_code) { + if (/^\@end/) { + eval "package w3mdoc; $code"; + $in_code = 0; + next; + } + $code .= $_; + next; + } + if (/^\@define/) { + $in_def = 1; + next; + } + if (/^\@code/) { + $in_code = 1; + $code = ""; + next; + } + if (s/^\@include\s+//) { + s/\s+$//; + &make_doc($_); + next; + } + if (/^\@/) { + die("unknown command: $_"); + } + s/(\\@|\@(\w+(\.\w+|\(\w+\)))\@)/&w3mdoc::SUB($1)/eg; + print; + } + close(F); +} + diff --git a/w3mbookmark.c b/w3mbookmark.c new file mode 100644 index 0000000..61b4d8c --- /dev/null +++ b/w3mbookmark.c @@ -0,0 +1,239 @@ +#ifdef __EMX__ +#include <stdlib.h> +#endif +#include <stdio.h> +#include "config.h" +#include "Str.h" +#include "indep.h" +#include "textlist.h" +#include "parsetag.h" + +#include "gcmain.c" + +#if LANG == JA +static char *bkmark_src1 = "<html><head><title>Bookmark Registration</title>\n\ +<body><h1>�֥å��ޡ�������Ͽ</h1>\n\n" +#ifdef __EMX__ +"<form method=get action=\"file://%s/w3mbookmark.exe\">\n\n" +#else +"<form method=get action=\"file://%s/w3mbookmark\">\n\n" +#endif +"<input type=hidden name=mode value=register>\n\ +<input type=hidden name=bmark value=\"%s\">\n\ +<table cellpadding=0>\n"; + +static char *bkmark_src2 = "<tr><td>New Section:</td><td><input type=text name=newsection width=60></td></tr>\n\ +<tr><td>URL:</td><td><input type=text name=url value=\"%s\" width=60></td></tr>\n\ +<tr><td>Title:</td><td><input type=text name=title value=\"%s\" width=60></td></tr>\n\ +<tr><td><input type=submit name=submit value=\"��Ͽ\"></td>\n\ +</table>\n\ +<input type=hidden name=cookie value=\"%s\"> +</form> +</body></html>\n"; +static char *default_section = "̤ʬ��"; +#else /* LANG != JA */ +static char *bkmark_src1 = "<html><head><title>Bookmark Registration</title>\n\ +<body><h1>Register to my bookmark</h1>\n\n" +#ifdef __EMX__ +"<form method=get action=\"file://%s/w3mbookmark.exe\">\n\n" +#else +"<form method=get action=\"file://%s/w3mbookmark\">\n\n" +#endif +"<input type=hidden name=mode value=register>\n\ +<input type=hidden name=bmark value=\"%s\">\n\ +<table cellpadding=0>\n"; + +static char *bkmark_src2 = "<tr><td>New Section:</td><td><input type=text name=newsection width=60></td></tr>\n\ +<tr><td>URL:</td><td><input type=text name=url value=\"%s\" width=60></td></tr>\n\ +<tr><td>Title:</td><td><input type=text name=title value=\"%s\" width=60></td></tr>\n\ +<tr><td><input type=submit name=submit value=\"ADD\"></td>\n\ +</table>\n\ +<input type=hidden name=cookie value=\"%s\"> +</form> +</body></html>\n"; +static char *default_section = "Miscellaneous"; +#endif /* LANG != JA */ + +#define FALSE 0 +#define T 1 + +static char end_section[] = "<!--End of section (do not delete this comment)-->\n"; + +char *Local_cookie; + +#ifdef __EMX__ +static char * +lib_dir() +{ + char *value = getenv("W3M_LIB_DIR"); + return value ? value : LIB_DIR; +} +#else +#define lib_dir() LIB_DIR +#endif + +void +print_bookmark_panel(char *bmark, char *url, char *title) +{ + Str tmp, tmp2; + FILE *f; + char *p; + + printf("Content-Type: text/html\n\n"); + printf(bkmark_src1, lib_dir(), bmark); + if ((f = fopen(bmark, "r")) != NULL) { + printf("<tr><td>Section:<td><select name=\"section\">\n"); + while (tmp = Strfgets(f), tmp->length > 0) { + if (Strncasecmp_charp(tmp, "<h2>", 4) == 0) { + p = tmp->ptr + 4; + tmp2 = Strnew(); + while (*p && *p != '<') + Strcat_char(tmp2, *p++); + printf("<option value=\"%s\">%s</option>", tmp2->ptr, tmp2->ptr); + } + } + printf("</select>\n"); + } + printf(bkmark_src2, htmlquote_str(url), htmlquote_str(title),Local_cookie); +} + +/* create new bookmark */ +static int +create_new_bookmark(char *bmark, char *section, char *title, char *url, char *mode) +{ + FILE *f; + f = fopen(bmark, mode); + if (f == NULL) { + printf("\nCan't open bookmark %s\n",bmark); + return FALSE; + } + else { + fprintf(f, "<html><head><title>Bookmarks</title></head>\n"); + fprintf(f, "<body>\n<h1>Bookmarks</h1>\n"); + fprintf(f, "<h2>%s</h2>\n<ul>\n", section); + fprintf(f, "<li><a href=\"%s\">%s</a>\n", url, title); + fprintf(f, end_section); + fprintf(f, "</ul>\n</body>\n</html>\n"); + fclose(f); + } + return TRUE; +} + +int +insert_bookmark(char *bmark, struct parsed_tagarg *data) +{ + char *url, *title, *section; + FILE *f; + TextList *tl = newTextList(); + int section_found = 0; + int bmark_added = 0; + Str tmp, section_tmp; + + url = tag_get_value(data, "url"); + title = tag_get_value(data, "title"); + section = tag_get_value(data, "newsection"); + if (section == NULL || *section == '\0') + section = tag_get_value(data, "section"); + if (section == NULL || *section == '\0') + section = default_section; + + if (url == NULL || *url == '\0' || + title == NULL || *title == '\0') { + /* Bookmark not added */ + return FALSE; + } + url = htmlquote_str(url); + title = htmlquote_str(title); + section = htmlquote_str(section); + + f = fopen(bmark, "r"); + if (f == NULL) + return create_new_bookmark(bmark,section,title,url,"w"); + + section_tmp = Sprintf("<h2>%s</h2>\n", section); + for (;;) { + tmp = Strfgets(f); + if (tmp->length == 0) + break; + if (Strcasecmp(tmp, section_tmp) == 0) + section_found = 1; + if (section_found && !bmark_added && Strcmp_charp(tmp, end_section) == 0) { + pushText(tl, Sprintf("<li><a href=\"%s\">%s</a>\n", url, title)->ptr); + bmark_added = 1; + } + if (!bmark_added && Strcasecmp_charp(tmp, "</body>\n") == 0) { + pushText(tl, Sprintf("<h2>%s</h2>\n<ul>\n", section)->ptr); + pushText(tl, Sprintf("<li><a href=\"%s\">%s</a>\n", url, title)->ptr); + pushText(tl, end_section); + pushText(tl, "</ul>\n"); + } + pushText(tl, tmp->ptr); + } + fclose(f); + if (!bmark_added) { + /* Bookmark not added; perhaps the bookmark file is ill-formed */ + /* In this case, a new bookmark is appeneded after the bookmark file */ + return create_new_bookmark(bmark,section,title,url,"a"); + } + f = fopen(bmark, "w"); + while (tl->nitem) { + fputs(popText(tl), f); + } + fclose(f); + return TRUE; +} + +int +MAIN(int argc, char *argv[], char **envp) +{ + extern char *getenv(); + char *qs; + struct parsed_tagarg *cgiarg; + char *mode; + char *bmark; + char *url; + char *title; + char *sent_cookie; + + if ((qs = getenv("QUERY_STRING")) == NULL) { + printf("Content-Type: text/plain\n\n"); + printf("Incomplete Request: no QUERY_STRING\n"); + exit(1); + } + + cgiarg = cgistr2tagarg(qs); + mode = tag_get_value(cgiarg, "mode"); + bmark = expandPath(tag_get_value(cgiarg, "bmark")); + url = tag_get_value(cgiarg, "url"); + title = tag_get_value(cgiarg, "title"); + if (bmark == NULL || url == NULL) { + /* incomplete request */ + printf("Content-Type: text/plain\n\n"); + printf("Incomplete Request: QUERY_STRING=%s\n",qs); + exit(1); + } + Local_cookie = getenv("LOCAL_COOKIE"); + sent_cookie = tag_get_value(cgiarg,"cookie"); + if (Local_cookie == NULL) { + /* Local cookie not provided: maybe illegal invocation */ + Local_cookie = ""; + } + if (mode && !strcmp(mode, "panel")) { + if (title == NULL) + title = ""; + print_bookmark_panel(bmark, url, title); + } + else if (mode && !strcmp(mode, "register")) { + printf("Content-Type: text/plain\n"); + if (sent_cookie == NULL || Local_cookie[0] == '\0' || + strcmp(sent_cookie,Local_cookie) != 0) { + /* local cookie doesn't match: It may be an illegal invocation */ + printf("\nBookmark not added: local cookie doesn't match\n"); + } + else if (insert_bookmark(bmark, cgiarg)) { + printf("w3m-control: BACK\n"); + printf("w3m-control: BACK\n\n"); + } + } + return 0; +} diff --git a/w3mhelp-lynx_en.html b/w3mhelp-lynx_en.html new file mode 100644 index 0000000..18cd652 --- /dev/null +++ b/w3mhelp-lynx_en.html @@ -0,0 +1,133 @@ +<HTML> +<HEAD> +<TITLE>w3m help page</TITLE> +</HEAD> +<BODY> +<CENTER> +******* +<A HREF="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/"> +w3m</A> + (WWW-wo-Miru) Version beta by +<A HREF="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">A.ITO</A> ********<BR> + ***** Key assign table ***** +</CENTER> + +<A HREF="w3mhelp-lynx_ja.html">Japanese</A> + +<H3>Page/Cursor motion</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>SPC,C-v<TD>Forward page +<TR><TD>b,ESC v<TD>Previous page +<TR><TD>l<TD>Cursor right +<TR><TD>h<TD>Cursor left +<TR><TD>j<TD>Cursor down +<TR><TD>k<TD>Cursor up +<TR><TD>J<TD>Roll up one line +<TR><TD>K<TD>Roll down one line +<TR><TD>><TD>Shift screen right +<TR><TD><<TD>Shift screen left +<TR><TD>.<TD>Shift screen one column right +<TR><TD>,<TD>Shift screen one column left +<TR><TD>G<TD>Go to the specified line +<TR><TD>TAB,C-n,Down arrow<TD>Move to next hyperlink +<TR><TD>ESC TAB,C-p,Up arrow<TD>Move to previous link +</table> + + +<H2>Hyperlink operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>RET, C-f, Right arrow<TD>Follow hyperlink +<TR><TD>d, ESC RET<TD>Save link to file +<TR><TD>u<TD>Peek link URL +<TR><TD>I<TD>View inline image +<TR><TD>ESC I<TD>Save inline image to file +<TR><TD>:<TD>Mark URL-like strings as anchors +<TR><TD>ESC :<TD>Mark Message-ID-like strings as news anchors +<TR><TD>c<TD>Peek current URL +<TR><TD>=<TD>Display infomation about current document +<TR><TD>F<TD>Render frame +<TR><TD>M<TD>Browse current document using external browser +(use 2M and 3M to invoke second and third browser) +<TR><TD>ESC M<TD>Browse link using external browser +(use 2ESC M and 3ESC M to invoke second and third browser) +</table> + +<H2>File/Stream operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>g,U<TD>Open URL +<TR><TD>V<TD>View new file +<TR><TD>@<TD>Execute shell command and load +<TR><TD>#<TD>Execute shell command and browse +</table> + +<H2>Buffer operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>B, C-b, Left arrow<TD>Back to the previous buffer +<TR><TD>\<TD>View HTML source +<TR><TD>s, C-h<TD>Select buffer +<TR><TD>E<TD>Edit buffer source +<TR><TD>R, C-r<TD>Reload buffer +<TR><TD>S, p<TD>Save buffer +<TR><TD>ESC s<TD>Save source +<TR><TD>ESC e<TD>Edit buffer image +</table> + +<H2>Buffer selection mode</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>k, C-p<TD>Select previous buffer +<TR><TD>j, C-n<TD>Select next buffer +<TR><TD>D<TD>Delect current buffer +<TR><TD>RET<TD>Go to the selected buffer +</table> + +<H2>Bookmark operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>v, ESC b<TD>Load bookmark +<TR><TD>a, ESC a<TD>Add current to bookmark +</table> + +<H2>Search</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>/<TD>Search forward +<TR><TD>?<TD>Search backward +<TR><TD>n<TD>Search next +</table> + +<H2>Mark operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>C-SPC<TD>Set/unset mark +<TR><TD>ESC p<TD>Go to previous mark +<TR><TD>ESC n<TD>Go to next mark +<TR><TD>"<TD>Mark by regular expression +</table> + +<H2>Miscellany</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>!<TD>Execute shell command +<TR><TD>H<TD>Help (load this file) +<TR><TD>o<TD>Set option +<TR><TD>C-k<TD>Show cookie jar +<TR><TD>C-c<TD>Stop +<TR><TD>C-z<TD>Suspend +<TR><TD>q<TD>Quit (with confirmation, if you like) +<TR><TD>Q<TD>Quit without confirmation +</table> + +<H2>Line-edit mode</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>C-f<TD>Move cursor forward +<TR><TD>C-b<TD>Move cursor backward +<TR><TD>C-h<TD>Delete previous character +<TR><TD>C-d<TD>Delete current character +<TR><TD>C-k<TD>Kill everything after cursor +<TR><TD>C-u<TD>Kill everything before cursor +<TR><TD>C-a<TD>Move to the top of line +<TR><TD>C-e<TD>Move to the bottom of line +<TR><TD>C-p<TD>Fetch the previous string from the history list +<TR><TD>C-n<TD>Fetch the next string from the history list +<TR><TD>TAB,SPC<TD>Complete filename +<TR><TD>RETURN<TD>Accept +</table> +<HR> +</body> +</html> diff --git a/w3mhelp-lynx_ja.html b/w3mhelp-lynx_ja.html new file mode 100644 index 0000000..f3abddf --- /dev/null +++ b/w3mhelp-lynx_ja.html @@ -0,0 +1,143 @@ +<HTML> +<HEAD> +<TITLE>w3m �إ�ץڡ���</TITLE> +</HEAD> + +<BODY> +<CENTER> + ******* + <A HREF="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/">w3m</A> + (WWW-wo-Miru) Version beta by + <A HREF="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">A.ITO(��ƣ��§)</A> + ******** + <BR> + ***** lynx-like ����������� ***** +</CENTER> + +<A HREF="w3mhelp-lynx_en.html">English</A> + +<H3>�ڡ���/���������ư</H3> +<table cellpadding=0> +<TR><TD>SPC,C-v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>b,ESC v<TD>���Υڡ�����ɽ�����ޤ��� +<TR><TD>l<TD>��������˰�ư���ޤ��� +<TR><TD>h<TD>��������˰�ư���ޤ��� +<TR><TD>j<TD>��������˰�ư���ޤ��� +<TR><TD>k<TD>����������˰�ư���ޤ��� +<TR><TD>J<TD>���̤�1�Ծ�˥��������뤷�ޤ��� +<TR><TD>K<TD>���̤�1�Բ��˥��������뤷�ޤ��� +<TR><TD>><TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD><<TD>�������Τˤ��餷�ޤ���(ɽ�����Ƥˤ��餹) +<TR><TD>C-a<TD>ʸ��Τ����Ф��ιԤ˰�ư���ޤ��� +<TR><TD>C-e<TD>ʸ��Τ����ФιԤ˰�ư���ޤ��� +<TR><TD>G<TD>���̲��ǹ��ֹ�����Ϥ��������ǻ��ꤷ���Ԥ˰�ư���ޤ��� +������ $ �����Ϥ���ȡ��ǽ��Ԥ˰�ư���ޤ��� +<TR><TD>TAB, C-n, �����<TD>���Υ�˰�ư���ޤ��� +<TR><TD>ESC TAB, C-p, �����<TD>���Υ�˰�ư���ޤ��� +</table> + +<H3>�ϥ��ѡ�������</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>RET, C-f, �����<TD>���ߥ������뤬�������ؤ����ʸ����ɤߤ��ߤޤ��� +<TR><TD>d, ESC RET<TD>���ߥ������뤬�������ؤ����ʸ���ե��������¸���ޤ��� +<TR><TD>u<TD>���ߥ������뤬�������ؤ����URL��ɽ�����ޤ��� +<TR><TD>I<TD>���ߥ������뤬�������б����������ɽ�����ޤ��� +<TR><TD>ESC I<TD>���ߥ������뤬�������ؤ�������ե��������¸���ޤ��� +<TR><TD>:<TD>URL����ʸ������ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ��� +�ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>ESC :<TD>Message-ID����ʸ�����news: �Υ�ˤ��ޤ������ε�ǽ�ϡ�HTML�Ǥʤ�ʸ����ɤ�Ǥ���Ȥ��ˤ�ͭ���Ǥ��� +<TR><TD>c<TD>���ߤ�ʸ���URL��ɽ�����ޤ��� +<TR><TD>=<TD>���ߤ�ʸ��˴ؤ�������ɽ�����ޤ��� +<TR><TD>F<TD><FRAMESET>��ޤ�ʸ���ɽ�����Ƥ���Ȥ��ˡ�<FRAME> +�����λؤ�ʣ����ʸ���1�Ĥ�ʸ����Ѵ�����ɽ�����ޤ��� +<TR><TD>M<TD>���߸��Ƥ���ڡ��������֥饦����Ȥä�ɽ�����ޤ��� +2M, 3M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +<TR><TD>ESC M<TD>���ߤΥ��������֥饦����Ȥä�ɽ�����ޤ��� +2ESC M, 3ESC M ��2���ܤ�3���ܤΥ֥饦����Ȥ��ޤ��� +</table> + +<H3>�ե������URL�ط������</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>g, U<TD>URL����ꤷ�Ƴ����ޤ��� +<TR><TD>V<TD>��������ե��������ꤷ�Ƴ����ޤ��� +<TR><TD>@<TD>���ޥ�ɤ�¹Ԥ�����̤������ɤ�Ǥ���ɽ�����ޤ��� +<TR><TD>#<TD>���ޥ�ɤ�¹Ԥ�����̤��ɤߤ��ߤʤ���ɽ�����ޤ��� +</table> + +<H3>�Хåե����</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>B, C-b, �����<TD>���߸��Ƥ���Хåե���������������ΥХåե���ɽ�����ޤ��� +<TR><TD>\<TD>HTML�Υ�������ɽ�����ޤ��� +<TR><TD>s, C-h<TD>�Хåե�����⡼�ɤ�����ޤ��� +<TR><TD>E<TD>���߸��Ƥ���Хåե�����������ե�����ξ�硤���Υե�����ǥ������Խ����ޤ������ǥ�����λ�����塤���Υե����������ɤ߹��ߤޤ��� +<TR><TD>R, C-r<TD>�Хåե�������ɤ߹��ߤޤ��� +<TR><TD>S, p<TD>�Хåե���ɽ�����Ƥ�ե��������¸���ޤ��� +<TR><TD>ESC s<TD>HTML�Υ�������ե��������¸���ޤ���v �ǥ�������ɽ������ S �� +��¸����ΤȤۤ�Ʊ���Ǥ�����ESC s ����¸�����ե�����ϴ��������ɤ����ꥸ�ʥ�� +�ޤޤǤ���Τ��Ф��ơ�v S ����¸����ȸ���ɽ���˻ȤäƤ�����������ɤ��Ѵ����� +����¸����ޤ��� +<TR><TD>ESC e<TD>����ɽ������Ƥ���Хåե���ɽ������Ƥ�������Τޤ� +���ǥ������Խ����ޤ��� +</table> + +<H3>�Хåե�����⡼��</H3> +"s" �ǥХåե�����⡼�ɤ����ä��Ȥ��Υ������Ǥ��� +<table cellpadding=0> +<TR><TD WIDTH=100>k,C-p<TD>��ľ�ΥХåե������ޤ��� +<TR><TD>j,C-n<TD>��IJ��ΥХåե������ޤ��� +<TR><TD>D<TD>�������Ƥ���Хåե��������ޤ��� +<TR><TD>RET<TD>�������Ƥ���Хåե���ɽ�����ޤ��� +</table> + +<H3>�֥å��ޡ������</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>v, ESC b<TD>�֥å��ޡ������ɤ߹��ߤޤ��� +<TR><TD>a, ESC a<TD>���߸��Ƥ���ڡ�����֥å��ޡ������ɲä��ޤ��� +</table> + +<H3>����</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>/<TD>���ߤΥ���������֤���ե����������˸����ä�����ɽ�������ޤ��� +<TR><TD>?<TD>���ߤΥ���������֤���ե��������Ƭ�˸����ä�����ɽ�������ޤ��� +<TR><TD>n<TD>�������ޤ��� +</table> + +<H3>�ޡ������</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>C-SPC<TD>�ޡ��������������ޤ����ޡ�����ȿžɽ������ޤ��� +<TR><TD>ESC p<TD>������Υޡ����˰�ư���ޤ��� +<TR><TD>ESC n<TD>��ĸ�Υޡ����˰�ư���ޤ��� +<TR><TD>"<TD>����ɽ���ǻ��ꤵ�줿ʸ��������ƥޡ������ޤ��� +</table> + +<H3>����¾</H3> +<table cellpadding=0> +<TR><TD WIDTH=100>!<TD>�����륳�ޥ�ɤ�¹Ԥ��ޤ��� +<TR><TD>H, ?<TD>�إ�ץե������ɽ�����ޤ��� +<TR><TD>o<TD>���ץ��������ѥͥ��ɽ�����ޤ��� +<TR><TD>C-k</TD> <TD>���å���������ɽ��</TD></TR> +<TR><TD>C-c<TD>ʸ����ɤ߹��ߤ����Ǥ��ޤ��� +<TR><TD>C-z</TD><TD>�����ڥ�ɤ��ޤ���</TD></TR> +<TR><TD>q<TD>w3m��λ���ޤ������ץ���������ˤ�äơ���λ���뤫�ɤ�����ǧ���ޤ��� +<TR><TD>Q<TD>��ǧ������w3m��λ���ޤ��� +</table> + +<H3>���Խ��⡼��</H3> +���̤κDz��Ԥ�ʸ��������Ϥ������ͭ���ʥ������Ǥ��� +<table cellpadding=0> +<TR><TD WIDTH=100>C-f<TD>��������˰�ư���ޤ��� +<TR><TD>C-b<TD>��������˰�ư���ޤ��� +<TR><TD>C-h<TD>���������ľ����ʸ���������ޤ��� +<TR><TD>C-d<TD>����������֤�ʸ���������ޤ��� +<TR><TD>C-k<TD>����������֤����������ޤ��� +<TR><TD>C-u<TD>����������֤������������ޤ��� +<TR><TD>C-a<TD>ʸ�������Ƭ�˰�ư���ޤ��� +<TR><TD>C-e<TD>ʸ����κǸ�˰�ư���ޤ��� +<TR><TD>SPC<TD>�ե�����̾���ϻ��ˡ��ե�����̾���䴰���ޤ��� +<TR><TD>RETURN<TD>���Ϥ�λ���ޤ��� +</table> + + + +</body> +</html> diff --git a/w3mhelp-w3m_en.html b/w3mhelp-w3m_en.html new file mode 100644 index 0000000..c91fdec --- /dev/null +++ b/w3mhelp-w3m_en.html @@ -0,0 +1,132 @@ +<HTML> +<HEAD> +<TITLE>w3m help page</TITLE> +</HEAD> +<BODY> +<CENTER> +******* +<A HREF="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/"> +w3m</A> + (WWW-wo-Miru) Version beta by +<A HREF="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">A.ITO</A> ********<BR> + ***** Key assign table ***** +</CENTER> + +<A HREF="w3mhelp-w3m_ja.html">Japanese</A> + +<H2>Page/Cursor motion</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>SPC,C-v<TD>Forward page +<TR><TD>b,ESC v<TD>Backward page +<TR><TD>l,C-f<TD>Cursor right +<TR><TD>h,C-b<TD>Cursor left +<TR><TD>j,C-n<TD>Cursor down +<TR><TD>k,C-p<TD>Cursor up +<TR><TD>J<TD>Roll up one line +<TR><TD>K<TD>Roll down one line +<TR><TD>w<TD>Go to next word +<TR><TD>W<TD>Go to previous word +<TR><TD>><TD>Shift screen right +<TR><TD><<TD>Shift screen left +<TR><TD>.<TD>Shift screen one column right +<TR><TD>,<TD>Shift screen one column left +<TR><TD>g<TD>Go to the first line +<TR><TD>G<TD>Go to the last line +<TR><TD>ESC g<TD>Go to specified line +<TR><TD>TAB<TD>Move to next hyperlink +<TR><TD>C-u,ESC TAB<TD>Move to previous hyperlink +<TR><TD>[<TD>Go to the first link +<TR><TD>]<TD>Go to the last link +</table> + +<H2>Hyperlink operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>RET<TD>Follow hyperlink +<TR><TD>a, ESC RET<TD>Save link to file +<TR><TD>u<TD>Peek link URL +<TR><TD>I<TD>View inline image +<TR><TD>ESC I<TD>Save inline image to file +<TR><TD>:<TD>Mark URL-like strings as anchors +<TR><TD>ESC :<TD>Mark Message-ID-like strings as news anchors +<TR><TD>c<TD>Peek current URL +<TR><TD>=<TD>Display infomation about current document +<TR><TD>F<TD>Render frame +<TR><TD>M<TD>Browse current document using external browser +(use 2M and 3M to invoke second and third browser) +<TR><TD>ESC M<TD>Browse link using external browser +(use 2ESC M and 3ESC M to invoke second and third browser) +</table> + +<H2>File/Stream operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>U<TD>Open URL +<TR><TD>V<TD>View new file +<TR><TD>@<TD>Execute shell command and load +<TR><TD>#<TD>Execute shell command and browse +</table> + +<H2>Buffer operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>B<TD>Back to the previous buffer +<TR><TD>v<TD>View HTML source +<TR><TD>s<TD>Select buffer +<TR><TD>E<TD>Edit buffer source +<TR><TD>R<TD>Reload buffer +<TR><TD>S<TD>Save buffer +<TR><TD>ESC s<TD>Save source +<TR><TD>ESC e<TD>Edit buffer image +</table> + +<H2>Bookmark operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>ESC b<TD>Load bookmark +<TR><TD>ESC a<TD>Add current to bookmark +</table> + +<H2>Search</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>/,C-s<TD>Search forward +<TR><TD>?,C-r<TD>Search backward +<TR><TD>n<TD>Search next +<TR><TD>N<TD>Search previous +</table> + +<H2>Mark operation</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>C-SPC<TD>Set/unset mark +<TR><TD>ESC p<TD>Go to previous mark +<TR><TD>ESC n<TD>Go to next mark +<TR><TD>"<TD>Mark by regular expression +</table> + +<H2>Miscellany</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>!<TD>Execute shell command +<TR><TD>H<TD>Help (load this file) +<TR><TD>o<TD>Set option +<TR><TD>C-k<TD>Show cookie jar +<TR><TD>C-c<TD>Stop +<TR><TD>C-z<TD>Suspend +<TR><TD>q<TD>Quit (with confirmation, if you like) +<TR><TD>Q<TD>Quit without confirmation +</table> + +<H2>Line-edit mode</H2> +<table cellpadding=0> +<TR><TD WIDTH=100>C-f<TD>Move cursor forward +<TR><TD>C-b<TD>Move cursor backward +<TR><TD>C-h<TD>Delete previous character +<TR><TD>C-d<TD>Delete current character +<TR><TD>C-k<TD>Kill everything after cursor +<TR><TD>C-u<TD>Kill everything before cursor +<TR><TD>C-a<TD>Move to the top of line +<TR><TD>C-e<TD>Move to the bottom of line +<TR><TD>C-p<TD>Fetch the previous string from the history list +<TR><TD>C-n<TD>Fetch the next string from the history list +<TR><TD>TAB,SPC<TD>Complete filename +<TR><TD>RETURN<TD>Accept +</table> +<HR> +</BODY> +</HTML> + diff --git a/w3mhelp-w3m_ja.html b/w3mhelp-w3m_ja.html new file mode 100644 index 0000000..d3906c1 --- /dev/null +++ b/w3mhelp-w3m_ja.html @@ -0,0 +1,132 @@ +<HTML> +<HEAD> +<TITLE>w3m �إ�� �ڡ���</TITLE> +</HEAD> + +<BODY> +<CENTER> + ******* + <A HREF="http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/">w3m</A> + (WWW-wo-Miru) Version beta by + <A HREF="mailto:aito@ei5sun.yz.yamagata-u.ac.jp">A.ITO(��ƣ��§)</A> + ******** + <BR> + ***** ����������� ***** +</CENTER> + +<A HREF="w3mhelp-w3m_en.html">English</A> + +<H2>�ڡ���/���������ư</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>SPC,C-v</TD> <TD>���ڡ���</TD></TR> + <TR> <TD>b,ESC v</TD> <TD>���ڡ���</TD></TR> + <TR> <TD>l,C-f</TD> <TD>��������ذ�ư</TD></TR> + <TR> <TD>h,C-b</TD> <TD>��������ذ�ư</TD></TR> + <TR> <TD>j,C-n</TD> <TD>��������ذ�ư</TD></TR> + <TR> <TD>k,C-p</TD> <TD>����������ذ�ư</TD></TR> + <TR> <TD>J</TD><TD>��Ծ�˥���������(�ʤ�)</TD></TR> + <TR> <TD>K</TD><TD>��Բ��˥���������(���)</TD></TR> + <TR> <TD>w</TD><TD>����ñ��˰�ư</TD></TR> + <TR> <TD>W</TD><TD>����ñ��˰�ư</TD></TR> + <TR> <TD>></TD> <TD>���˰����ʬ���ե�</TD></TR> + <TR> <TD><</TD> <TD>���˰����ʬ���ե�</TD></TR> + <TR> <TD>.</TD> <TD>���˰�ʸ��ʬ���ե�</TD></TR> + <TR> <TD>,</TD> <TD>���˰�ʸ��ʬ���ե�</TD></TR> + <TR> <TD>g</TD> <TD>�ڡ�������Ƭ�Ԥ˰�ư</TD></TR> + <TR> <TD>G</TD> <TD>�ڡ����κǽ��Ԥ˰�ư</TD></TR> + <TR> <TD>ESC g</TD> <TD>����Ԥ˰�ư</TD></TR> + <TR> <TD>TAB</TD> <TD>���Υϥ��ѡ���˰�ư</TD></TR> + <TR> <TD>C-u, ESC TAB</TD> <TD>���Υϥ��ѡ���˰�ư</TD></TR> + <TR> <TD>[</TD> <TD>�ǽ�Υϥ��ѡ���˰�ư</TD></TR> + <TR> <TD>]</TD> <TD>�Ǹ�Υϥ��ѡ���˰�ư</TD></TR> +</TABLE> + +<H2>�ϥ��ѡ�������</H2> + +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>RET</TD> <TD>�������벼�Υ������</TD></TR> + <TR> <TD>a, ESC RET</TD> <TD>������ʸ���ե��������¸</TD></TR> + <TR> <TD>ESC I</TD> <TD>�����β�����ե��������¸</TD></TR> + <TR> <TD>u</TD> <TD>�����URL��ɽ��</TD></TR> + <TR> <TD>I</TD> <TD>����饤�����ɽ��</TD></TR> + <TR> <TD>:</TD> <TD>URL�Τ褦��ʸ�����Ȥ��ƥޡ���</TD></TR> + <TR> <TD>ESC :</TD> <TD>Message-ID�Τ褦��ʸ�����Ȥ��ƥޡ���</TD></TR> + <TR> <TD>c</TD> <TD>���ڡ�����URL��ɽ��</TD></TR> + <TR> <TD>=</TD> <TD>���ɥ�����Ȥξ����ɽ��</TD></TR> + <TR> <TD>F</TD> <TD>�ե졼���ɽ������</TD></TR> + <TR> <TD>M</TD> <TD>���ڡ��������֥饦����ɽ������(2M,3M��2���ܤ�3���ܤΥ֥饦����ƤӤ���)</TD></TR> + <TR> <TD>ESC M</TD> <TD>���������֥饦����ɽ������(2ESC M,3ESC M��2���ܤ�3���ܤΥ֥饦����ƤӤ���)</TD></TR> +</TABLE> + +<H2>�ե�����/���ȥ�����</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>U</TD> <TD>URL�����</TD></TR> + <TR> <TD>V</TD> <TD>�ե������</TD></TR> + <TR> <TD>@</TD> <TD>�������ư���ɤ߹���</TD></TR> + <TR> <TD>#</TD> <TD>�������ư���ɤ߹���</TD></TR> +</TABLE> + +<H2>�Хåե����</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>B</TD> <TD>���ΥХåե��˰�ư</TD></TR> + <TR> <TD>v</TD> <TD>HTML��������ɽ��</TD></TR> + <TR> <TD>s</TD> <TD>�Хåե�������</TD></TR> + <TR> <TD>E</TD> <TD>�Хåե��Υ��������Խ�</TD></TR> + <TR> <TD>R</TD> <TD>�Хåե�����ɤ߹���</TD></TR> + <TR> <TD>S</TD> <TD>�Хåե�����¸</TD></TR> + <TR> <TD>ESC s</TD> <TD>HTML����������¸</TD></TR> + <TR> <TD>ESC e</TD> <TD>�Хåե���ɽ����������Խ�</TD></TR> +</TABLE> + +<H2>�֥å��ޡ������</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>ESC b</TD> <TD>�֥å��ޡ������ɤ߹���</TD></TR> + <TR> <TD>ESC a</TD> <TD>���ڡ�����֥å��ޡ������ɲ�</TD></TR> +</TABLE> + +<H2>����</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>/,C-s</TD> <TD>��������</TD></TR> + <TR> <TD>?,C-r</TD> <TD>��������</TD></TR> + <TR> <TD>n</TD> <TD>����</TD></TR> + <TR> <TD>N</TD> <TD>����</TD></TR> +</TABLE> + +<H2>�ޡ������</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>C-SPC</TD> <TD>�ޡ���������/�õ�</TD></TR> + <TR> <TD>ESC p</TD> <TD>���Υޡ����ذ�ư</TD></TR> + <TR> <TD>ESC n</TD> <TD>���Υޡ����ذ�ư</TD></TR> + <TR> <TD>"</TD> <TD>����ɽ���ˤ��ޡ���</TD></TR> +</TABLE> + +<H2>����¾</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>!</TD> <TD>������μ¹�</TD></TR> + <TR> <TD>H</TD> <TD>�إ��(���Υե������ɽ��)</TD></TR> + <TR> <TD>o</TD> <TD>���ץ��������</TD></TR> + <TR> <TD>C-k</TD> <TD>���å���������ɽ��</TD></TR> + <TR> <TD>C-c</TD> <TD>ʸ����ɤ߹��ߤ�����</TD></TR> + <TR> <TD>C-z</TD> <TD>�����ڥ��</TD></TR> + <TR> <TD>q</TD> <TD>w3m��λ(��ǧ����)</TD></TR> + <TR> <TD>Q</TD> <TD>w3m��λ(��ǧ�ʤ�)</TD></TR> +</TABLE> + +<H2>���Խ��⡼��</H2> +<TABLE CELLPADDING=0> + <TR> <TD WIDTH=100>C-f</TD> <TD>��������ذ�ư</TD></TR> + <TR> <TD>C-b</TD> <TD>��������ذ�ư</TD></TR> + <TR> <TD>C-h</TD> <TD>����ʸ������</TD></TR> + <TR> <TD>C-d</TD> <TD>����������֤�ʸ������</TD></TR> + <TR> <TD>C-k</TD> <TD>��������θ��������ƺ��</TD></TR> + <TR> <TD>C-u</TD> <TD>������������ޤǤ����ƺ��</TD></TR> + <TR> <TD>C-a</TD> <TD>��Ƭ�˰�ư</TD></TR> + <TR> <TD>C-e</TD> <TD>�����ذ�ư</TD></TR> + <TR> <TD>C-p</TD> <TD>�ҥ��ȥ꤫��������ʸ�������Ф�</TD></TR> + <TR> <TD>C-n</TD> <TD>�ҥ��ȥ꤫�鼡��ʸ�������Ф�</TD></TR> + <TR> <TD>TAB,SPC</TD> <TD>�ե�����̾���䴰</TD></TR> + <TR> <TD>RETURN</TD> <TD>���Ͻ�λ</TD></TR> +</TABLE> +<HR> +</BODY> +</HTML> diff --git a/w3mhelperpanel.c b/w3mhelperpanel.c new file mode 100644 index 0000000..ab8eef6 --- /dev/null +++ b/w3mhelperpanel.c @@ -0,0 +1,195 @@ +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include "config.h" +#include "Str.h" +#include "indep.h" +#include "textlist.h" +#include "parsetag.h" +#include "myctype.h" + +#include "gcmain.c" + +#if LANG == JA +#define MSG_TITLE "�����ӥ塼�����Խ�" +#define MSG_NEW_ENTRY "������Ͽ" +#define MSG_TYPE "�ǡ���������" +#define MSG_COMMAND "�������ޥ��" +#define MSG_REGISTER "��Ͽ" +#define MSG_DELETE "���" +#define MSG_DOIT "�¹�" +#else /* LANG != JA */ +#define MSG_TITLE "External Viewers" +#define MSG_NEW_ENTRY "New Entry" +#define MSG_TYPE "Type" +#define MSG_COMMAND "Command" +#define MSG_REGISTER "Register" +#define MSG_DELETE "Delete" +#define MSG_DOIT "Do it" +#endif /* LANG != JA */ + +char *local_cookie; + +void +extractMailcapEntry(char *mcap_entry, char **type, char **cmd) +{ + int j, k; + + while (*mcap_entry && IS_SPACE(*mcap_entry)) + mcap_entry++; + for (j = 0; mcap_entry[j] && mcap_entry[j] != ';' && !IS_SPACE(mcap_entry[j]); + j++); + *type = allocStr(mcap_entry, j); + if (mcap_entry[j] == ';') + j++; + while (mcap_entry[j] && IS_SPACE(mcap_entry[j])) + j++; + *cmd = allocStr(&mcap_entry[j], 0); +} + +static void +bye(const char*action,const char*mailcap) +{ printf("Content-Type: text/plain\n\n%s %s\n", action, mailcap); + exit(1); +} + +void +printMailcapPanel(char *mailcap) +{ + FILE *f; + Str tmp; + char *type, *viewer; + + if ((f = fopen(mailcap, "rt")) == NULL) { + if(errno!=ENOENT) + bye("Can't open",mailcap); + + if(!(f=fopen(mailcap,"a+"))) /* if $HOME/.mailcap is not found, make it now! */ + bye("Can't open",mailcap); + + { char* SysMailcap=getenv("SYS_MAILCAP"); + FILE* s = fopen(SysMailcap?SysMailcap:"/etc/mailcap","r"); + if (s) { + char buffer[256]; + while(fgets(buffer,sizeof buffer,s)) /* Copy system mailcap to */ + fputs(buffer,f); /* users' new one */ + fclose(s); + rewind(f); + } + } + } + printf("Content-Type: text/html\n\n"); + printf("<html><head><title>External Viewer Setup</title></head><body><h1>%s</h1>\n", MSG_TITLE); +#ifdef __EMX__ + printf("<form method=get action=\"file:///$LIB/w3mhelperpanel.exe\">\n"); +#else + printf("<form method=get action=\"file:///$LIB/w3mhelperpanel\">\n"); +#endif + printf("<input type=hidden name=mode value=edit>\n"); + printf("<input type=hidden name=cookie value=\"%s\">\n",local_cookie); + printf("%s: %s=<input type=text name=newtype><br>%s=<input type=text name=newcmd><br><input type=submit name=submit value=\"%s\">\n", + MSG_NEW_ENTRY, MSG_TYPE, MSG_COMMAND, MSG_REGISTER); + printf("<p><hr width=50%%><p><table border='0' cellpadding='0'><tr><th> <th><b>%s</b><th><b>%s</b>\n", + MSG_TYPE, MSG_COMMAND); + while (tmp = Strfgets(f), tmp->length > 0) { + if (tmp->ptr[0] == '#') + continue; + Strchop(tmp); + extractMailcapEntry(tmp->ptr, &type, &viewer); + printf("<tr valign=top><td><td>%s<td>%s<td>", htmlquote_str(type), htmlquote_str(viewer)); + printf("<input type=checkbox name=delete value=\"%s\">%s\n", + htmlquote_str(type), MSG_DELETE); + } + printf("</table><input type=submit name=submit value=\"%s\"></form></body></html>\n", + MSG_DOIT); +} + +void +editMailcap(char *mailcap, struct parsed_tagarg *args) +{ + TextList *t = newTextList(); + TextListItem *ti; + FILE *f; + Str tmp; + char *type, *viewer; + struct parsed_tagarg *a; + int delete_it; + + if ((f = fopen(mailcap, "rt")) == NULL) + bye("Can't open",mailcap); + + while (tmp = Strfgets(f), tmp->length > 0) { + if (tmp->ptr[0] == '#') + continue; + Strchop(tmp); + extractMailcapEntry(tmp->ptr, &type, &viewer); + delete_it = 0; + for (a = args; a != NULL; a = a->next) { + if (!strcmp(a->arg, "delete") && !strcmp(a->value, type)) { + delete_it = 1; + break; + } + } + if (!delete_it) + pushText(t, Sprintf("%s;\t%s\n", type, viewer)->ptr); + } + type = tag_get_value(args, "newtype"); + viewer = tag_get_value(args, "newcmd"); + if (type != NULL && *type != '\0' && viewer != NULL && *viewer != '\0') + pushText(t, Sprintf("%s;\t%s\n", type, viewer)->ptr); + fclose(f); + if ((f = fopen(mailcap, "w")) == NULL) + bye("Can't write to",mailcap); + + for (ti = t->first; ti != NULL; ti = ti->next) + fputs(ti->ptr, f); + fclose(f); + printf("Content-Type: text/plain\n"); + printf("w3m-control: BACK\nw3m-control: BACK\n"); + printf("w3m-control: INIT_MAILCAP\n"); +} + +int +MAIN(int argc, char *argv[], char **envp) +{ + Str mailcapfile; + extern char *getenv(); + char *qs; + struct parsed_tagarg *cgiarg; + char *mode; + char *sent_cookie; + + if ((qs = getenv("QUERY_STRING")) == NULL) + exit(1); + + cgiarg = cgistr2tagarg(qs); + mode = tag_get_value(cgiarg, "mode"); + local_cookie = getenv("LOCAL_COOKIE"); + mailcapfile = Strnew_charp(expandPath(RC_DIR)); + Strcat_charp(mailcapfile, "/mailcap"); + + if (mode && !strcmp(mode, "edit")) { + char *referer; + /* check if I can edit my mailcap */ + if ((referer = getenv("HTTP_REFERER")) != NULL) { + if (strncmp(referer,"file://",7) != 0 && + strncmp(referer,"exec://",7) != 0) { + /* referer is not file: nor exec: */ + bye("It may be an illegal execution\n referer=",referer); + } + } + sent_cookie = tag_get_value(cgiarg,"cookie"); + if (local_cookie == NULL || sent_cookie == NULL || + strcmp(local_cookie,sent_cookie) != 0) { + /* Local cookie doesn't match */ + bye("Local cookie doesn't match: It may be an illegal execution",""); + } + /* edit mailcap */ + editMailcap(mailcapfile->ptr, cgiarg); + } + else { + /* initial panel */ + printMailcapPanel(mailcapfile->ptr); + } + return 0; +} @@ -0,0 +1,11 @@ +#!/bin/sh +arg=$1 +for d in `echo $PATH | sed -e 's/:/ /g'` +do + if [ -x $d/$arg ]; then + echo $d/$arg + exit 0 + fi +done +echo "${arg}: Command not found." +exit 1 |