|
|
BSD 4.3tahoe
% Physical screen hacking cluster, for editors, etc.
# extend
screen = cluster is
% mode changing and initialization functions
init, % sets up (first time, or new terminal)
enter_image_mode, % set up terminal
leave_image_mode, % restores the terminal
destroy, % finishes up
% option setting functions
set_padding, % pad output
set_scroll, % do scrolling (if poss)
set_keypad_mode, % enter/exit alternate keypad mode
set_highlight, % underline/invert
recolor, % change colors
% display functions
clear, % clear screen and home up
display_line, % display a line of an environment
redisplay_line, % redisplay line as is
display_chars, % display chars at end of line
update_line, % update data to correspond with screen
set_cursor_pos, % set the cursor position
get_cursor_pos, % get the cursor position
scroll, % scroll a region
full_scroll, % scroll entire screen
bell, % bell
highlight, % highlight
redisplay, % hack redisplay
unmapped, % hack icon
% information returning functions
position, % pixel -> char coordinates
get_padding, % padding on ?
get_screen_size, % returns length and width of screen
fetch, % fetch a line
id_lines_poss, % ins/del lines possible ?
scrolling_poss, % full screen scrolling possible ?
should_id_lines, % makes decision about ins/del lines
should_scroll % makes decision about scrolling
ldata = record[line: act, % screen line, as chars
len: int, % len of real string
lim: int, % pos of last non-space char in line
str: string] % the actual string
% invariant property of ldatas:
% size(line) = hsize - 1
% len = _calc_hpos(str, string$size(str) + 1)
% lim < i < hsize => line[i] = ' '
% the chars in line correspond to the chars in str
% the chars in lines[j] correspond to the chars on the
% screen on line j (the presence of an ! determined by len)
rep = null
al = array[ldata]
qi = sequence[int]
qs = sequence[string]
zapc = array_zap[char]
shiftc = array_shift[char]
shiftl = array_shift[ldata]
repll = array_replace[ldata]
events = KeyPressed + ButtonPressed + ButtonReleased +
ExposeRegion + ExposeCopy + UnmapWindow
own done: bool := false
% screen data base
own lines: al % array of line data
own holder: act % for display_line (new line)
own hlim: int % limit for holder
own temp: al % for scrolling
own xline: act % display_chars hack
own xvpos: int
own xhpos: int
own xmpos: int
own image: bool % in image mode?
own f: x_font
own fheight: int
own fwidth: int
own of: x_font
own oheight: int
own owidth: int
own textpix: int
own clearpix: int
own planemask: int
own nobit: x_bitmap
own w: x_window
own ow: x_window
own mousepix: int
own mousefunc: int
own chpos: int
own cdisp: bool
own crcols: colordeflist
own hlcols: colordeflist
own hlmode: bool
own high: bool
own hvpos: int
own hhpos: int
% terminal properties
own vsize: int % # of lines (0 to vsize-1)
own hsize: int % # of cols (0 to hsize-1)
own vsize1: int % vsize-1
own hsize1: int % hsize-1
% cursor info (-1 means unknown)
own vpos: int % current vertical pos
own hpos: int % current horizontal pos
init = proc (options: qs)
if ~done
then lines := al$create(0)
holder := act$create(0)
hlim := -1
temp := al$create(0)
xhpos := 0
xmpos := -1
image := false
display: string := ""
myname: string := _get_xjname()
font: string := x_default(myname, "BodyFont")
except when not_found: font := "8x13" end
revvid: bool := x_default(myname, "ReverseVideo") = "on"
except when not_found: revvid := false end
bwidth: int := int$parse(x_default(myname, "BorderWidth"))
except when not_found, overflow, bad_format: bwidth := 2 end
spec: string := "=80x24"
cfore: string := x_default(myname, "Foreground")
except when not_found: cfore := "" end
cback: string := x_default(myname, "Background")
except when not_found: cback := "" end
ccurs: string := x_default(myname, "Cursor")
except when not_found: ccurs := "" end
chigh: string := x_default(myname, "Highlight")
except when not_found: chigh := "" end
cbdr: string := x_default(myname, "Border")
except when not_found: cbdr := "" end
mfore: string := x_default(myname, "Mouse")
except when not_found: mfore := "" end
mousefunc := int$parse(x_default(myname, "MouseFunction"))
except when not_found, overflow, bad_format: mousefunc := GXcopy end
icon: bool := x_default(myname, "BitmapIcon") = "on"
except when not_found: icon := false end
for opt: string in qs$elements(options) do
if opt = "-rv"
then revvid := true
elseif opt = "-i"
then icon := true
elseif string$indexs("-fn=", opt) = 1
then font := string$rest(opt, 5)
elseif string$indexs("-fg=", opt) = 1
then cfore := string$rest(opt, 5)
elseif string$indexs("-bg=", opt) = 1
then cback := string$rest(opt, 5)
elseif string$indexs("-cr=", opt) = 1
then ccurs := string$rest(opt, 5)
elseif string$indexs("-hl=", opt) = 1
then chigh := string$rest(opt, 5)
elseif string$indexs("-bd=", opt) = 1
then cbdr := string$rest(opt, 5)
elseif string$indexs("-ms=", opt) = 1
then mfore := string$rest(opt, 5)
elseif opt[1] = '='
then spec := opt
else if opt[1] = '-'
then opt := string$rest(opt, 2) end
if string$indexc(':', opt) ~= 0
then display := opt
else font := opt
end
end
end
x_display$init(display)
except when error, failure (why: string):
_chan$puts(_chan$error_output(), why || "\r\n",
false)
quit_()
end
f := x_font$create(font)
except when error (*):
_chan$puts(_chan$error_output(), "bad font\r\n",
false)
quit_()
end
clearmap, bdrmap: x_pixmap
if revvid
then textpix := WhitePixel
bdrmap := x_display$white()
clearpix := BlackPixel
clearmap := x_display$black()
else textpix := BlackPixel
bdrmap := x_display$black()
clearpix := WhitePixel
clearmap := x_display$white()
end
mousepix := textpix
crcols := colordeflist$new()
hlcols := colordeflist$new()
begin
if x_display$cells() > 2 cand
(~string$empty(cfore) cor ~string$empty(cback) cor
~string$empty(ccurs) cor ~string$empty(chigh))
then pixs: pixellist
if string$empty(ccurs) cand string$empty(chigh)
then pixs, planemask := x_display$alloc_cells(
1, 1, false)
clearpix := pixs[1]
textpix := clearpix + planemask
else pixs, planemask := x_display$alloc_cells(
2, 1, false)
clearpix := pixs[1]
textpix := pixs[2]
end
mousepix := textpix
r, g, b: int
if string$empty(cback)
then r, g, b := x_display$query_color(clearpix)
else r, g, b := x_parse_color(cback)
end
x_display$store_color(clearpix, r, g, b)
if ~string$empty(ccurs) cor ~string$empty(chigh)
then x_display$store_color(textpix + planemask,
r, g, b)
end
clearmap := x_pixmap$tile(clearpix)
if string$empty(cfore)
then r, g, b := x_display$query_color(textpix)
else r, g, b := x_parse_color(cfore)
end
x_display$store_color(textpix, r, g, b)
if ~string$empty(chigh)
then hr, hg, hb: int := x_parse_color(chigh)
colordeflist$addh(hlcols,
colordef${pixel: clearpix +
planemask,
red: hr,
green: hg,
blue: hb})
end
if ~string$empty(ccurs)
then r, g, b := x_parse_color(ccurs) end
colordeflist$addh(crcols,
colordef${pixel: clearpix +
planemask,
red: r,
green: g,
blue: b})
if ~string$empty(ccurs) cor ~string$empty(chigh)
then x_display$store_color(clearpix + planemask,
r, g, b)
end
else planemask := 1
end
if x_display$cells() > 2
then if ~string$empty(cbdr)
then r, g, b: int := x_parse_color(cbdr)
bdrmap := x_pixmap$tile(x_display$alloc_color(
r, g, b))
end
if ~string$empty(mfore)
then r, g, b: int := x_parse_color(mfore)
mousepix := x_display$alloc_color(r, g, b)
end
end
end except when undefined, bad_format:
_chan$puts(_chan$error_output(), "bad color\r\n",
false)
quit_()
end
first, last: char
base: int
fixed: bool
fwidth, fheight, first, last, base, fixed := x_font$query(f)
w, hsize, vsize := x_tcons(myname, clearmap, bdrmap,
spec, "=80x24+1+1",
f, fwidth, fheight, 2,
6, 6, bwidth)
if icon
then of := x_font$create("nil2")
owidth, oheight, first, last, base, fixed :=
x_font$query(of)
ow := x_window$create(0, 0, hsize * owidth + 2,
vsize * oheight + 2, clearmap,
x_display$root(), 2, bdrmap)
ow.input := events
w.icon := ow
else ow := x_window$none()
end except when error (*):
_chan$puts(_chan$error_output(), "bad font\r\n",
false)
quit_()
end
w.name := myname
x_window$set_resize(w, 2, fwidth, 2, fheight)
w.input := events - ExposeRegion
x_window$map(w)
w.input := events
new_cursor()
nobit := x_bitmap$none()
vsize1 := vsize - 1
hsize1 := hsize - 1
chpos := 0
cdisp := false
hlmode := false
high := false
done := true
else leave_image_mode()
end
enter_image_mode()
end init
enter_image_mode = proc ()
if image
then return end
sx, sy, sw, sh, wb, ms, wk: int, iw: x_window := x_window$query(w)
hsize := int$max((sw + fwidth - 3) / fwidth, 6)
hsize1 := hsize - 1
vsize := int$max((sh + fheight - 3) / fheight, 6)
vsize1 := vsize - 1
nw: int := hsize * fwidth + 2
nh: int := vsize * fheight + 2
if nh ~= sh cor nw ~= sw
then x_window$change(w, nw, nh)
if ow ~= x_window$none()
then x_window$change(ow, hsize * owidth + 2,
vsize * oheight + 2)
end
end
output$reset()
vpos := -1 % force positioning
hpos := -1
image := true
end enter_image_mode
leave_image_mode = proc ()
if image
then input$reset()
image := false
end
end leave_image_mode
destroy = proc ()
leave_image_mode()
x_window$destroy(w)
done := false
end destroy
set_padding = proc (b: bool)
end set_padding
set_scroll = proc (b: bool)
end set_scroll
set_keypad_mode = proc (b: bool) returns (bool)
return(false)
end set_keypad_mode
set_highlight = proc (h, b: bool)
hlmode := b
if h
then w.input := events + MouseMoved
if ~colordeflist$empty(hlcols)
then x_display$store_colors(hlcols) end
else w.input := events
if ~colordeflist$empty(hlcols)
then x_display$store_colors(crcols) end
end
end set_highlight
recolor = proc (white: bool) returns (bool)
if textpix > WhitePixel
then return(false) end
if white
then textpix := BlackPixel
clearpix := WhitePixel
w.background := x_display$white()
w.border := x_display$black()
else textpix := WhitePixel
clearpix := BlackPixel
w.background := x_display$black()
w.border := x_display$white()
end
mousepix := textpix
x_window$clear(w)
new_cursor()
return(true)
end recolor
clear = proc ()
ovsize: int := al$size(lines)
x_window$clear(w)
x_flush()
deltav: int := vsize - ovsize
deltah: int := hsize1 - act$size(holder)
limit: int := ovsize - 1
if deltav < 0
then limit := vsize1 end
% clear out char arrays, and auxiliary info
for i: int in int$from_to(0, limit) do
line: ldata := lines[i]
lim: int := line.lim
chars: act := line.line
zapc(chars, 0, lim+1, ' ')
if deltah < 0
then act$trim(chars, 0, hsize1)
else for j: int in int$from_to_by(deltah, 1, -1) do
act$addh(chars, ' ')
end
end
line.lim := -1
line.len := 0
line.str := ""
end
if deltav < 0
then al$trim(lines, 0, vsize)
else for i: int in int$from_to_by(deltav, 1, -1) do
line: ldata :=
ldata${line: act$fill(0, hsize1, ' '),
lim: -1,
len: 0,
str: ""}
al$addh(lines, line)
end
end
if deltah < 0
then act$trim(holder, 0, hsize1)
if hlim >= hsize1
then hlim := hsize - 2 end
else for i: int in int$from_to_by(deltah, 1, -1) do
act$addh(holder, ' ')
end
end
vpos := -1 % force positioning
cdisp := false
set_cursor_pos(0, 0, true)
end clear
display_line = proc (s: string, lpos: int) returns (bool) signals (bounds)
line: ldata := lines[lpos]
resignal bounds
if s = line.str
then return(false) end
if cdisp
then forget_cursor() end
new: act := holder
nlim, newlen: int := _calc_hpos_copy(s, new)
if hlim >= newlen
then zapc(new, newlen, hlim - newlen + 1, ' ') end
old: act := line.line
oldlen: int := line.len
olim: int := line.lim
excl: char := ' '
if oldlen >= hsize
then excl := '!' end
mlim: int := nlim
mpos: int := _diff_scan(new, old, 0, mlim)
if hpos ~= mpos cor vpos ~= lpos cor cdisp
then reposition(lpos, mpos) end
outa(new, mpos, mlim - mpos + 1)
hpos := mlim + 1
mpos := hpos
if mlim < olim
then % keol needed
vpos := lpos
hpos := mpos
chpos := mpos
if excl = ' '
then mlim := olim + 1
else mlim := hsize
excl := ' '
end
clear_region(lpos, mpos, 1, mlim - mpos)
end
dexcl: char := ' ' % desired excl place char
if newlen >= hsize
then dexcl := '!' end
if dexcl ~= excl
then reposition(lpos, hsize1)
outc(dexcl)
hpos := hsize
reposition(lpos, hsize1)
end
holder := old
hlim := olim
line.line := new
line.lim := nlim
line.len := newlen
line.str := s
return(true)
end display_line
redisplay_line = proc (lpos: int)
line: ldata := lines[lpos]
except when bounds: return end
if cdisp
then forget_cursor() end
vpos := -1
s: string := line.str
zapc(line.line, 0, hsize1, '\177')
if line.len >= hsize
then line.len := hsize1
else line.len := hsize
end
line.lim := hsize1
line.str := "\177"
display_line(s, lpos)
end redisplay_line
display_chars = proc (nvpos, ohpos, nhpos: int, chars: act, mhpos: int)
xline := chars
xvpos := nvpos
if xhpos > xmpos
then xhpos := ohpos end
xmpos := nhpos
reposition(nvpos, ohpos)
outa(chars, ohpos, nhpos - ohpos)
hpos := nhpos
if nhpos < mhpos
then clear_region(nvpos, nhpos, 1, mhpos - nhpos) end
display_cursor()
end display_chars
update_line = proc (s: string, lpos: int) signals (bounds)
xmpos := -1
line: ldata := lines[lpos]
resignal bounds
nlim, newlen: int := _calc_hpos_copy(s, line.line)
if line.lim >= newlen
then zapc(line.line, newlen, line.lim - newlen + 1, ' ') end
line.lim := nlim
line.len := newlen
line.str := s
end update_line
reposition = proc (nvpos, nhpos: int)
if nhpos >= hsize
then nhpos := hsize1 end
if cdisp
then forget_cursor() end
if vpos ~= nvpos cor hpos ~= nhpos
then vpos := nvpos
hpos := nhpos
chpos := hpos
end
end reposition
set_cursor_pos = proc (nvpos, nhpos: int, doit: bool)
if nhpos >= hsize
then nhpos := hsize1 end
if nvpos ~= vpos cor nhpos ~= hpos
then if cdisp
then forget_cursor() end
vpos := nvpos
hpos := nhpos
chpos := hpos
end
if ~cdisp
then display_cursor() end
if doit
then x_flush() end
end set_cursor_pos
forget_cursor = proc ()
x_window$pix_fill(w, 0, nobit, 1 + hpos * fwidth, 1 + vpos * fheight,
fwidth, fheight, GXinvert, planemask)
cdisp := false
end forget_cursor
display_cursor = proc ()
x_window$pix_fill(w, 0, nobit, 1 + hpos * fwidth, 1 + vpos * fheight,
fwidth, fheight, GXinvert, planemask)
cdisp := true
end display_cursor
get_cursor_pos = proc () returns (int, int)
return(vpos, hpos)
end get_cursor_pos
position = proc (x, y: int) returns (int, int)
return((x - 1) / fwidth, (y - 1) / fheight)
end position
get_padding = proc () returns (bool)
return(false)
end get_padding
get_screen_size = proc () returns (int, int)
return(vsize, hsize)
end get_screen_size
fetch = proc (lpos: int) returns (string) signals (bounds)
return(lines[lpos].str)
resignal bounds
end fetch
id_lines_poss = proc () returns (bool)
return(true)
end id_lines_poss
scrolling_poss = proc () returns (bool)
return(false)
end scrolling_poss
should_id_lines = proc (top, bot, delta, num_saved: int) returns (bool)
return(num_saved > 0)
end should_id_lines
scroll = proc (top, bot, delta: int)
d: int := int$abs(delta)
topd: int := top + d
pos: int := bot - d + 1
numshift: int := pos - top
input$copy_wait()
if cdisp
then forget_cursor() end
max: int := 0
for i: int in int$from_to(top, bot) do
line: ldata := lines[i]
if lines[i].len >= hsize
then max := hsize
break
end
max := int$max(max, line.lim)
end
max := max + 1
vpos := -1
if delta < 0
then % are scrolling up
copy_region(topd, 0, top, 0, pos - top, max)
clear_region(pos, 0, d, max)
repll(temp, 0, al$size(temp), lines, top, d)
for ltemp: ldata in al$elements(temp) do
zapc(ltemp.line, 0, ltemp.lim+1, ' ')
ltemp.lim := -1
ltemp.len := 0
ltemp.str := ""
end
shiftl(lines, topd, numshift, delta)
repll(lines, pos, d, temp, 0, d)
else % scrolling down
copy_region(top, 0, topd, 0, pos - top, max)
clear_region(top, 0, d, max)
repll(temp, 0, al$size(temp), lines, pos, d)
for ltemp: ldata in al$elements(temp) do
zapc(ltemp.line, 0, ltemp.lim+1, ' ')
ltemp.lim := -1
ltemp.len := 0
ltemp.str := ""
end
shiftl(lines, top, numshift, delta)
repll(lines, top, d, temp, 0, d)
end
end scroll
should_scroll = proc (delta, num_saved, num_saved0: int) returns (bool)
signals (clear, id_lines)
return(false)
end should_scroll
full_scroll = proc (delta: int) returns (bool)
return(false)
end full_scroll
bell = proc ()
x_feep(0)
end bell
highlight = proc (flag: bool)
if ~cdisp
then return end
if ~flag
then if high
then dohigh(vpos, hpos, hvpos, hhpos)
high := false
end
return
end
h, v: int, sw: x_window := x_window$query_mouse(w)
h := int$min(int$max(0, (h - 1) / fwidth), hsize)
v := int$min(int$max(0, (v - 1) / fheight), vsize)
if h > 0 cand ~(v = xvpos cand xmpos > xhpos cand h > xhpos)
then l: ldata := lines[v]
if h < l.len cand string$indexc(l.line[h - 1], " ^&!") > 0
then i: int := int$max(h + 2, string$size(l.str) + 1)
oh: int := h
while true do
h := _calc_hpos(l.str, i)
if h <= oh
then break end
i := i - 1
end
end
end except when bounds: end
if ~high
then dohigh(vpos, hpos, v, h)
elseif v = hvpos cand h = hhpos
then return
elseif (v > vpos cor (v = vpos cand h > hpos)) cand
(hvpos > vpos cor (hvpos = vpos cand hhpos > hpos))
then dohigh(hvpos, hhpos, v, h)
elseif (v < vpos cor (v = vpos cand h < hpos)) cand
(hvpos < vpos cor (hvpos = vpos cand hhpos < hpos))
then dohigh(v, h, hvpos, hhpos)
else dohigh(vpos, hpos, hvpos, hhpos)
dohigh(vpos, hpos, v, h)
end
high := true
hvpos := v
hhpos := h
end highlight
dohigh = proc (v1, h1, v2, h2: int)
if v1 = v2
then if h1 > h2
then h1, h2 := h2, h1 end
elseif v1 > v2
then v1, v2 := v2, v1
h1, h2 := h2, h1
end
if h1 < 0
then h1 := 0
elseif h1 > hsize1
then v1 := v1 + 1
h1 := 0
end
if v1 < 0
then v1 := 0
h1 := 0
elseif v1 > vsize1
then v1 := vsize1
h1 := hsize1
elseif v1 = vpos cand h1 = hpos
then h1 := h1 + 1 end
if v2 > vsize1
then v2 := vsize1
h2 := hsize1
elseif h2 > hsize
then h2 := hsize end
v: int
if hlmode
then v := 1 + v1 * fheight
else v := (v1 + 1) * fheight
end
while v1 < v2 do
h: int := lines[v1].len
if v1 = xvpos cand xmpos > xhpos
then h := xmpos end
if h1 < h
then dohigh1(v, h1, h) end
v := v + fheight
v1 := v1 + 1
h1 := 0
end
h2 := int$min(h2, lines[v2].len)
if v2 = xvpos cand xmpos > xhpos
then h2 := xmpos end
if h1 < h2
then dohigh1(v, h1, h2) end
end dohigh
dohigh1 = proc (v, h1, h2: int)
if hlmode
then x_window$pix_fill(w, 0, nobit, 1 + h1 * fwidth, v,
(h2 - h1) * fwidth, fheight, GXinvert,
planemask)
else x_window$line(w, 0, 1, 1,
1 + h1 * fwidth, v, h2 * fwidth, v, GXinvert,
planemask)
end
end dohigh1
redisplay = proc (win: x_window, x, y, width, height: int)
if win ~= w
then return end
h1: int := int$max(0, (x - 1) / fwidth)
h2: int := (x + width - 2) / fwidth
if h2 < h1
then return end
v1: int := int$max(0, (y - 1) / fheight)
v2: int := (y + height - 2) / fheight
if cdisp cand
vpos >= v1 cand vpos <= v2 cand hpos >= h1 cand hpos <= h2
then clear_region(vpos, hpos, 1, 1)
cdisp := false
end
ovpos: int := vpos
ohpos: int := hpos
for lpos: int in int$from_to(v1, v2) do
line: ldata := lines[lpos]
if line.lim < h1
then continue end
reposition(lpos, h1)
outa(line.line, h1, int$min(line.lim, h2) - h1 + 1)
if h2 >= hsize1 cand line.len >= hsize
then reposition(lpos, hsize1)
outc('!')
end
end except when bounds: end
if xmpos > xhpos cand xmpos > h1 cand xhpos <= h2 cand
xvpos >= v1 cand xvpos <= v2
then pos: int := int$max(xhpos, h1)
reposition(xvpos, pos)
outa(xline, pos, int$min(xmpos - 1, h2) - pos + 1)
end
set_cursor_pos(ovpos, ohpos, true)
end redisplay
unmapped = proc (win: x_window)
if win = w cand ow ~= x_window$none()
then w, ow := ow, w
f, of := of, f
fheight, oheight := oheight, fheight
fwidth, owidth := owidth, fwidth
redisplay(w, 1, 1, fwidth * hsize, fheight * vsize)
end
end unmapped
copy_region = proc (ovpos, ohpos, nvpos, nhpos, height, width: int)
x_window$move_area(w, 1 + ohpos * fwidth, 1 + ovpos * fheight,
width * fwidth, height * fheight,
1 + nhpos * fwidth, 1 + nvpos * fheight)
end copy_region
clear_region = proc (nvpos, nhpos, height, width: int)
x_window$pix_set(w, clearpix, 1 + nhpos * fwidth, 1 + nvpos * fheight,
width * fwidth, height * fheight)
end clear_region
outc = proc (c: char)
x_window$text(w, string$c2s(c), f, textpix, clearpix,
1 + chpos * fwidth, 1 + vpos * fheight)
chpos := chpos + 1
end outc
outa = proc (a: act, i, z: int) signals (bounds, negative_size)
x_window$texta(w, a, i, z, f, textpix, clearpix,
1 + chpos * fwidth, 1 + vpos * fheight)
chpos := chpos + z
end outa
new_cursor = proc ()
cursbits = "\003\000\005\000\011\000\021\000\041\000\101\000" ||
"\201\000\001\001\001\002\301\003\111\000\225\000" ||
"\223\000\040\001\040\001\300\000"
maskbits = "\003\000\007\000\017\000\037\000\077\000\177\000" ||
"\377\000\377\001\377\003\377\003\177\000\367\000" ||
"\363\000\340\001\340\001\300\000"
cursor: x_cursor := x_cursor$scons(11, 16,
cursbits, maskbits,
clearpix, mousepix, 1, 1, mousefunc)
w.cursor := cursor
if ow ~= x_window$none()
then ow.cursor := cursor end
x_cursor$destroy(cursor)
end new_cursor
end screen
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.