From 36bd1a796bc70b0842a065a7fc68e0479f0e40c4 Mon Sep 17 00:00:00 2001 From: mlokr Date: Sat, 6 Jul 2024 23:02:04 +0200 Subject: [PATCH] GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH, I did not delete unwanted relocations --- hblang/src/codegen.rs | 235 +++++++++----- hblang/tests/codegen_tests_c_strings.txt | 3 + sertion failed: (reloc.offset as usize) < len | 298 ++++++++++++++++++ 3 files changed, 459 insertions(+), 77 deletions(-) create mode 100644 sertion failed: (reloc.offset as usize) < len diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 8ca364a..01ea317 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -372,11 +372,11 @@ pub mod ty { const FLAG_OFFSET: u32 = std::mem::size_of::() as u32 * 8 - Self::FLAG_BITS; const INDEX_MASK: u32 = (1 << (32 - Self::FLAG_BITS)) - 1; - $vis const fn from_ty(ty: Id) -> Self { + $vis fn from_ty(ty: Id) -> Self { let (flag, index) = (ty.repr() >> Self::FLAG_OFFSET, ty.repr() & Self::INDEX_MASK); match flag { $(${index(0)} => Self::$variant(index),)* - _ => unreachable!(), + i => unreachable!("{i}"), } } @@ -480,6 +480,8 @@ struct Reloc { offset: Offset, sub_offset: u8, width: u8, + #[cfg(debug_assertions)] + shifted: bool, } impl Reloc { @@ -488,6 +490,18 @@ impl Reloc { offset, sub_offset, width, + #[cfg(debug_assertions)] + shifted: false, + } + } + + fn shifted(offset: u32, sub_offset: u8, width: u8) -> Self { + Self { + offset, + sub_offset, + width, + #[cfg(debug_assertions)] + shifted: true, } } @@ -511,13 +525,11 @@ impl Reloc { } fn write_offset(&self, code: &mut [u8], offset: i64) { + #[cfg(debug_assertions)] + assert!(self.shifted); let bytes = offset.to_ne_bytes(); - let slice = - &mut code[self.offset as usize + self.sub_offset as usize..][..self.width as usize]; - slice.copy_from_slice(&bytes[..self.width as usize]); - if slice.contains(&0x83) { - panic!() - } + let slice = &mut code[self.offset as usize + self.sub_offset as usize..]; + slice[..self.width as usize].copy_from_slice(&bytes[..self.width as usize]); } } @@ -551,9 +563,7 @@ impl Value { fn ty(ty: ty::Id) -> Self { Self { ty: ty::TYPE.into(), - loc: Loc::Ct { - value: (ty.repr() as u64).to_ne_bytes(), - }, + loc: Loc::ct((ty.repr() as u64).to_ne_bytes()), } } } @@ -619,9 +629,11 @@ impl Loc { } fn imm(value: u64) -> Self { - Self::Ct { - value: value.to_ne_bytes(), - } + Self::ct(value.to_ne_bytes()) + } + + fn ct(value: [u8; 8]) -> Self { + Self::Ct { value } } fn ty(ty: ty::Id) -> Self { @@ -747,14 +759,29 @@ impl ItemCtx { // } fn finalize(&mut self, output: &mut Output) { - self.stack.finalize_leaked(); - for rel in self.stack_relocs.drain(..) { - rel.apply_stack_offset(&mut output.code[self.snap.code..], &self.stack) + let len = output.code.len() as Offset; + let base = self.snap.code as Offset; + for reloc in output + .reloc_iter_mut(&self.snap) + .chain(&mut self.ret_relocs) + { + #[cfg(debug_assertions)] + { + if std::mem::replace(&mut reloc.shifted, true) { + panic!("reloc visited twice"); + } + } + reloc.offset += base; + debug_assert!(reloc.offset < len); + } + + self.stack.finalize_leaked(); + for rel in self.stack_relocs.drain(..) { + rel.apply_stack_offset(&mut output.code[base as usize..], &self.stack) } - let ret_offset = output.code.len() - self.snap.code; for rel in self.ret_relocs.drain(..) { - rel.apply_jump(&mut output.code[self.snap.code..], ret_offset as _); + rel.apply_jump(&mut output.code, len); } self.finalize_frame(output); @@ -798,9 +825,6 @@ impl ItemCtx { fn write_reloc(doce: &mut [u8], offset: usize, value: i64, size: u16) { let value = value.to_ne_bytes(); doce[offset..offset + size as usize].copy_from_slice(&value[..size as usize]); - if value.contains(&131) { - panic!(); - } } #[derive(PartialEq, Eq, Hash)] @@ -826,6 +850,7 @@ struct Sig { #[derive(Clone, Copy)] struct Func { + file: FileId, expr: ExprRef, sig: Option, offset: Offset, @@ -971,7 +996,7 @@ struct FTask { id: ty::Func, } -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)] pub struct Snapshot { code: usize, funcs: usize, @@ -1000,18 +1025,18 @@ impl Output { } fn emit(&mut self, (len, instr): (usize, [u8; instrs::MAX_SIZE])) { - // let name = instrs::NAMES[instr[0] as usize]; - // log::dbg!( - // "{:08x}: {}: {}", - // self.code.len(), - // name, - // instr - // .iter() - // .take(len) - // .skip(1) - // .map(|b| format!("{:02x}", b)) - // .collect::() - // ); + let name = instrs::NAMES[instr[0] as usize]; + log::dbg!( + "{:08x}: {}: {}", + self.code.len(), + name, + instr + .iter() + .take(len) + .skip(1) + .map(|b| format!("{:02x}", b)) + .collect::() + ); self.code.extend_from_slice(&instr[..len]); } @@ -1042,15 +1067,36 @@ impl Output { } fn pop(&mut self, stash: &mut Self, snap: &Snapshot) { - for rel in self.reloc_iter_mut(snap) { - rel.offset -= snap.code as Offset; - rel.offset += stash.code.len() as Offset; - } + // for rel in self.reloc_iter_mut(snap) { + // debug_assert!(snap.code < rel.offset as usize); + // rel.offset -= snap.code as Offset; + // rel.offset += stash.code.len() as Offset; + // } + + let init_code = stash.code.len(); stash.code.extend(self.code.drain(snap.code..)); - stash.funcs.extend(self.funcs.drain(snap.funcs..)); - stash.globals.extend(self.globals.drain(snap.globals..)); - stash.strings.extend(self.strings.drain(snap.strings..)); + stash.funcs.extend( + self.funcs.drain(snap.funcs..).inspect(|(_, rel)| { + debug_assert!(rel.offset as usize + init_code < stash.code.len()) + }), + ); + stash + .globals + .extend(self.globals.drain(snap.globals..).inspect(|(_, rel)| { + log::dbg!( + "reloc: {rel:?} {init_code} {} {} {}", + stash.code.len(), + self.code.len(), + snap.code + ); + debug_assert!(rel.offset as usize + init_code < stash.code.len()) + })); + stash + .strings + .extend(self.strings.drain(snap.strings..).inspect(|str| { + debug_assert!(str.reloc.offset as usize + init_code < stash.code.len()) + })); } fn trunc(&mut self, snap: &Snapshot) { @@ -1074,12 +1120,6 @@ impl Output { strings: self.strings.len(), } } - - fn emit_call(&mut self, func_id: ty::Func) { - let reloc = Reloc::new(self.code.len() as _, 3, 4); - self.funcs.push((func_id, reloc)); - self.emit(jal(RET_ADDR, ZERO, 0)); - } } #[derive(Default, Debug)] @@ -1222,6 +1262,7 @@ struct StringReloc { reloc: Reloc, range: std::ops::Range, } + impl StringReloc { fn range(&self) -> std::ops::Range { self.range.start as _..self.range.end as _ @@ -1250,7 +1291,7 @@ impl Codegen { } pub fn dump(mut self, out: &mut impl std::io::Write) -> std::io::Result<()> { - let reloc = Reloc::new(0, 3, 4); + let reloc = Reloc::shifted(0, 3, 4); self.output.funcs.push((0, reloc)); self.link(); out.write_all(&self.output.code) @@ -1463,7 +1504,7 @@ impl Codegen { self.report(pos, "string literal must end with null byte (for now)"); } - let reloc = Reloc::new(self.output.code.len() as _, 3, 4); + let reloc = Reloc::new(self.local_offset() as _, 3, 4); let start = self.string_data.len(); let report = |s: &Codegen, bytes: &std::str::Bytes, message| { @@ -1557,7 +1598,7 @@ impl Codegen { return Some(Value { ty, loc }); } E::Field { target, field } => { - let checkpoint = self.output.code.len(); + let checkpoint = self.local_snap(); let mut tal = self.expr(target)?; if let ty::Kind::Ptr(ty) = tal.ty.expand() { @@ -1576,13 +1617,14 @@ impl Codegen { }) } ty::Kind::Builtin(ty::TYPE) => { - self.output.code.truncate(checkpoint); + self.ci.free_loc(tal.loc); + self.pop_local_snap(checkpoint); match ty::Kind::from_ty(self.ty(target)) { ty::Kind::Module(idx) => Some(Value::ty( self.find_or_declare(target.pos(), idx, Err(field), "") .compress(), )), - _ => unimplemented!(), + e => unimplemented!("{e:?}"), } } smh => self.report( @@ -1670,13 +1712,14 @@ impl Codegen { E::Call { func: fast, args, .. } => { + log::dbg!("call {fast}"); let func_ty = self.ty(fast); let ty::Kind::Func(mut func_id) = func_ty.expand() else { self.report(fast.pos(), "can't call this, maybe in the future"); }; let func = self.tys.funcs[func_id as usize]; - let ast = self.cfile().clone(); + let ast = self.files[func.file as usize].clone(); let E::BinOp { right: &E::Closure { @@ -1730,6 +1773,7 @@ impl Codegen { let ct = || { let func_id = self.tys.funcs.len(); self.tys.funcs.push(Func { + file: func.file, offset: task::id(func_id), sig: Some(Sig { args, ret }), expr: func.expr, @@ -1772,7 +1816,11 @@ impl Codegen { log::dbg!("call ctx: {ctx:?}"); let loc = self.alloc_ret(sig.ret, ctx); - self.output.emit_call(func_id); + + let reloc = Reloc::new(self.local_offset(), 3, 4); + self.output.funcs.push((func_id, reloc)); + self.output.emit(jal(RET_ADDR, ZERO, 0)); + self.load_ret(sig.ret, &loc); return Some(Value { ty: sig.ret, loc }); } @@ -1826,8 +1874,9 @@ impl Codegen { }; self.expr_ctx(val, Ctx::default().with_ty(self.ci.ret).with_loc(loc))?; } - let off = self.local_offset(); - self.ci.ret_relocs.push(Reloc::new(off, 1, 4)); + self.ci + .ret_relocs + .push(Reloc::new(self.local_offset(), 1, 4)); self.output.emit(jmp(0)); None } @@ -1918,8 +1967,9 @@ impl Codegen { Some(Value::void()) } E::Break { .. } => { - let offset = self.local_offset(); - self.ci.loop_relocs.push(Reloc::new(offset, 1, 4)); + self.ci + .loop_relocs + .push(Reloc::shifted(self.local_offset(), 1, 4)); self.output.emit(jmp(0)); None } @@ -2214,10 +2264,10 @@ impl Codegen { fn handle_global(&mut self, id: ty::Global) -> Option { let ptr = self.ci.regs.allocate(); + let reloc = Reloc::new(self.local_offset(), 3, 4); let global = &mut self.tys.globals[id as usize]; - - let reloc = Reloc::new(self.output.code.len() as u32, 3, 4); self.output.globals.push((id, reloc)); + log::dbg!("{}", self.output.globals.len() - self.ci.snap.globals); self.output.emit(instrs::lra(ptr.get(), 0, 0)); Some(Value { @@ -2553,7 +2603,9 @@ impl Codegen { fn stack_reloc(&mut self, stack: &stack::Id, off: Offset, sub_offset: u8) -> u64 { log::dbg!("whaaaaatahack: {:b}", stack.repr()); let offset = self.local_offset(); - self.ci.stack_relocs.push(Reloc::new(offset, sub_offset, 8)); + self.ci + .stack_relocs + .push(Reloc::shifted(offset, sub_offset, 8)); Reloc::pack_srel(stack, off) } @@ -2616,6 +2668,7 @@ impl Codegen { let mut ci = ItemCtx { file: self.ci.file, id: self.ci.id, + ret: ty::TYPE.into(), ..self.pool.cis.pop().unwrap_or_default() }; ci.vars.append(&mut self.ci.vars); @@ -2623,9 +2676,16 @@ impl Codegen { let loc = self.ct_eval(ci, |s, prev| { s.output.emit_prelude(); - let ctx = Ctx::default().with_ty(ty::TYPE); - let Some(ret) = s.expr_ctx(expr, ctx) else { - s.report(expr.pos(), "type cannot be unreachable"); + if s.expr_ctx( + &Expr::Return { + pos: 0, + val: Some(expr), + }, + Ctx::default().with_ty(ty::TYPE), + ) + .is_some() + { + s.report(expr.pos(), "we fucked up"); }; let stash = s.complete_call_graph(); @@ -2635,18 +2695,11 @@ impl Codegen { s.ci.finalize(&mut s.output); s.output.emit(tx()); - let loc = match ret.loc { - Loc::Rt { ref reg, .. } => Ok(reg.get()), - Loc::Ct { value } => Err(value), - }; - s.ci.free_loc(ret.loc); - - loc + Ok(1) }); ty::Id::from(match loc { - Ok(reg) => self.ct.vm.read_reg(reg).cast::().to_ne_bytes(), - Err(ct) => ct, + Ok(reg) | Err(reg) => self.ct.vm.read_reg(reg).cast::().to_ne_bytes(), }) } @@ -2725,8 +2778,9 @@ impl Codegen { } => { let id = self.tys.funcs.len() as _; let func = Func { + file, offset: task::id(self.tasks.len()), - sig: 'b: { + sig: 'b: { let arg_base = self.tys.args.len(); for arg in args { let sym = find_symbol(&self.files[file as usize].symbols, arg.id); @@ -2744,7 +2798,7 @@ impl Codegen { let ret = self.ty(ret); Some(Sig { args, ret }) }, - expr: ExprRef::new(expr), + expr: ExprRef::new(expr), }; self.tys.funcs.push(func); @@ -2832,10 +2886,12 @@ impl Codegen { ci: ItemCtx, compile: impl FnOnce(&mut Self, &mut ItemCtx) -> Result, ) -> Result { + log::dbg!("eval"); let stash = self.pop_stash(); let mut prev_ci = std::mem::replace(&mut self.ci, ci); self.ci.snap = self.output.snap(); + debug_assert_eq!(self.ci.snap, prev_ci.snap); self.ci.task_base = self.tasks.len(); self.ci.regs.init(); @@ -2843,8 +2899,9 @@ impl Codegen { if ret.is_ok() { self.link(); + log::dbg!("{} {}", self.output.code.len(), self.ci.snap.code); let entry = &mut self.output.code[self.ci.snap.code] as *mut _ as _; - self.ct.vm.pc = hbvm::mem::Address::new(entry); + let prev_pc = std::mem::replace(&mut self.ct.vm.pc, hbvm::mem::Address::new(entry)); loop { match self.ct.vm.run().unwrap() { hbvm::VmRunOk::End => break, @@ -2853,13 +2910,17 @@ impl Codegen { hbvm::VmRunOk::Breakpoint => unreachable!(), } } + self.ct.vm.pc = prev_pc; } self.output.trunc(&self.ci.snap); self.pool.cis.push(std::mem::replace(&mut self.ci, prev_ci)); self.ci.snap = self.output.snap(); + debug_assert_eq!(self.pool.cis.last().unwrap().snap, self.ci.snap); self.push_stash(stash); + log::dbg!("eval-end"); + ret } @@ -2896,6 +2957,26 @@ impl Codegen { (self.output.code.len() - self.ci.snap.code) as u32 } + fn local_snap(&self) -> Snapshot { + Snapshot { + code: self.output.code.len() - self.ci.snap.code, + funcs: self.output.funcs.len() - self.ci.snap.funcs, + globals: self.output.globals.len() - self.ci.snap.globals, + strings: self.output.strings.len() - self.ci.snap.strings, + } + } + + fn pop_local_snap(&mut self, snap: Snapshot) { + self.output.code.truncate(snap.code + self.ci.snap.code); + self.output.funcs.truncate(snap.funcs + self.ci.snap.funcs); + self.output + .globals + .truncate(snap.globals + self.ci.snap.globals); + self.output + .strings + .truncate(snap.strings + self.ci.snap.strings); + } + fn pack_args(&mut self, pos: Pos, arg_base: usize) -> ty::Tuple { let needle = &self.tys.args[arg_base..]; if needle.is_empty() { diff --git a/hblang/tests/codegen_tests_c_strings.txt b/hblang/tests/codegen_tests_c_strings.txt index e69de29..198f4b3 100644 --- a/hblang/tests/codegen_tests_c_strings.txt +++ b/hblang/tests/codegen_tests_c_strings.txt @@ -0,0 +1,3 @@ +code size: 202 +ret: 13 +status: Ok(()) diff --git a/sertion failed: (reloc.offset as usize) < len b/sertion failed: (reloc.offset as usize) < len new file mode 100644 index 0000000..901a11c --- /dev/null +++ b/sertion failed: (reloc.offset as usize) < len @@ -0,0 +1,298 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^S _n Search for match in _n-th parenthesized subpattern. + ^W WRAP search if no match found. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-m_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + #_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ......... --line-numbers + Suppress line numbers in prompts and messages. + -N ......... --LINE-NUMBERS + Display line number at start of each line. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces, tabs and carriage returns. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + --exit-follow-on-close + Exit F command on a pipe when writer closes pipe. + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --header=[_N[,_M]] + Use N lines and M columns to display file headers. + --incsearch + Search file as each pattern character is typed in. + --intr=_C + Use _C instead of ^X to interrupt a read. + --line-num-width=_N + Set the width of the -N line number field to _N characters. + --modelines=_N + Read _N lines from the input file and look for vim modelines. + --mouse + Enable mouse input. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --no-number-headers + Don't give line numbers to header lines. + --no-search-headers + Don't search in header lines or columns. + --no-vbell + Disable the terminal's visual bell. + --redraw-on-quit + Redraw final screen when quitting. + --rscroll=_C + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --search-options=[EFKNRW-] + Set default options for every search. + --show-preproc-errors + Display a message if preprocessor exits with an error status. + --proc-backspace + Process backspaces for bold/underline. + --SPECIAL-BACKSPACE + Treat backspaces as control characters. + --proc-return + Delete carriage returns before newline. + --SPECIAL-RETURN + Treat carriage returns as control characters. + --proc-tab + Expand tabs to spaces. + --SPECIAL-TAB + Treat tabs as control characters. + --status-col-width=_N + Set the width of the -J status column to _N characters. + --status-line + Highlight or color the entire line containing a mark. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=_N + Each click of the mouse wheel moves _N lines. + --wordwrap + Wrap lines at spaces. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all.