From 438efcad8e59a9c2101410966919d7826b2e314e Mon Sep 17 00:00:00 2001 From: draccut Date: Tue, 15 Sep 2026 17:01:58 +0200 Subject: [PATCH] the code --- src/ccat_markdown.rs | 336 +++++++++++++++++++ src/col.rs | 51 +++ detect_filetype.rs => src/detect_filetype.rs | 0 src/highlighter.rs | 278 +++++++++++++++ src/keywords.rs | 141 ++++++++ language.rs => src/language.rs | 0 main.rs => src/main.rs | 0 src/specials.rs | 212 ++++++++++++ state.rs => src/state.rs | 0 9 files changed, 1018 insertions(+) create mode 100755 src/ccat_markdown.rs create mode 100755 src/col.rs rename detect_filetype.rs => src/detect_filetype.rs (100%) mode change 100644 => 100755 create mode 100755 src/highlighter.rs create mode 100755 src/keywords.rs rename language.rs => src/language.rs (100%) mode change 100644 => 100755 rename main.rs => src/main.rs (100%) mode change 100644 => 100755 create mode 100755 src/specials.rs rename state.rs => src/state.rs (100%) mode change 100644 => 100755 diff --git a/src/ccat_markdown.rs b/src/ccat_markdown.rs new file mode 100755 index 0000000..05f5429 --- /dev/null +++ b/src/ccat_markdown.rs @@ -0,0 +1,336 @@ +const MY_COLOR: u64 = 0x22223Bffffffu64; +const CARD_LEN: usize = 50; + +#[derive(Clone, Copy)] +pub struct MdMapping { + pub md_start: &'static str, + pub md_end: &'static str, + pub html_start: &'static str, + pub html_end: &'static str, + pub ansi_start: &'static str, + pub ansi_end: &'static str, +} + +static MD_MAP: &[MdMapping] = &[ + MdMapping { md_start: "# ", md_end: "", html_start: "

", html_end: "

", ansi_start: "\x1b[1;37m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "## ", md_end: "", html_start: "

", html_end: "

", ansi_start: "\x1b[1;36m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "### ", md_end: "", html_start: "

", html_end: "

", ansi_start: "\x1b[1;35m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "#### ", md_end: "", html_start: "

", html_end: "

", ansi_start: "\x1b[1;34m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "##### ", md_end: "", html_start: "
", html_end: "
", ansi_start: "\x1b[1;33m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "###### ", md_end: "", html_start: "
", html_end: "
", ansi_start: "\x1b[1;32m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "**", md_end: "**", html_start: "", html_end: "", ansi_start: "\x1b[1m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "__", md_end: "__", html_start: "", html_end: "", ansi_start: "\x1b[1m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "*", md_end: "*", html_start: "", html_end: "", ansi_start: "\x1b[3m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "_", md_end: "_", html_start: "", html_end: "", ansi_start: "\x1b[3m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "~~", md_end: "~~", html_start: "", html_end: "", ansi_start: "\x1b[9m", ansi_end: "\x1b[0m" }, + MdMapping { md_start: "`", md_end: "`", html_start: "", html_end: "", ansi_start: "\x1b[7m", ansi_end: "\x1b[0m" }, +]; + +fn log(message: &str) { + println!("{}", message); +} + +fn pp(res: &mut String, input: String) { + res.push_str(&input); +} + +fn pri(res: &mut String, col: u64, text: &str) { + let bg_r = ((col >> 40) & 0xFF) as u8; + let bg_g = ((col >> 32) & 0xFF) as u8; + let bg_b = ((col >> 24) & 0xFF) as u8; + let fg_r = ((col >> 16) & 0xFF) as u8; + let fg_g = ((col >> 8) & 0xFF) as u8; + let fg_b = (col & 0xFF) as u8; + pp(res, format!("\x1b[38;2;{};{};{}m", fg_r, fg_g, fg_b)); + pp(res, format!("\x1b[48;2;{};{};{}m", bg_r, bg_g, bg_b)); + pp(res, format!("{}", text)); + pp(res, format!("\x1b[0m")); +} + +fn draw_line(res: &mut String) { + let first = format!(" {} ", "=".repeat(CARD_LEN)); + res.push_str(&first); +} + +fn print_head(res: &mut String) { + #[allow(non_snake_case)] + let LL: String = String::from("\n"); + #[allow(non_snake_case)] + let SS: String = String::from(" "); + #[allow(non_snake_case)] + let QQ: String = String::from(" | "); + pp(res, SS.clone()); + pri(res, MY_COLOR, &format!("{: <50}", " ")); + pp(res, QQ.clone()); + pri(res, MY_COLOR, &format!("{: <50}", " ")); + pp(res, QQ); + pri(res, MY_COLOR, &format!("{: <50}", " ")); + pp(res, LL.clone()); + draw_line(res); + pp(res, LL); +} + +fn process_table_ansi(res: &mut String, lines: &[&str]) { + print_head(res); + for (_idx, line) in lines.iter().enumerate() { + if line.starts_with('|') && line.contains("---") { + continue; + } + let cells: Vec<&str> = line.split('|').collect(); + for index in 1..cells.len() { + pri(res, MY_COLOR, &format!("{: <50}", process_inline(cells[index], true))); + res.push_str(" | "); + } + res.push_str("\n"); + } + draw_line(res); + res.push_str("\n"); +} + +fn process_inline(text: &str, use_ansi: bool) -> String { + let mut result = text.to_string(); + for m in MD_MAP { + if !m.md_end.is_empty() { + let placeholder = format!("{}{}{}", m.md_start, "{}", m.md_end); + let replacement = if use_ansi { + format!("{}{}{}", m.ansi_start, "{}", m.ansi_end) + } else { + format!("{}{}{}", m.html_start, "{}", m.html_end) + }; + result = result.replace(&placeholder, &replacement); + } + } + result +} + +fn process_heading(line: &str, use_ansi: bool) -> Option { + for m in MD_MAP { + if line.starts_with(m.md_start) && m.md_end.is_empty() { + let content = line.trim_start_matches(m.md_start).trim(); + let processed = process_inline(content, use_ansi); + if use_ansi { + return Some(format!( + "\x1b[48;2;0;0;0m\x1b[38;2;255;255;255m{}\x1b[0m", + processed + )); + } else { + return Some(format!("{}{}{}", m.html_start, processed, m.html_end)); + } + } + } + None +} + +fn is_list_item(line: &str) -> Option<(usize, bool, String)> { + log(&format!("Checking for list item: '{}'", line)); + let trimmed = line.trim_start(); + let indent = line.len() - trimmed.len(); + if trimmed.starts_with("- ") || trimmed.starts_with("* ") || trimmed.starts_with("+ ") { + let content = trimmed[2..].trim().to_string(); + log(&format!("Unordered list item found: '{}'", content)); + return Some((indent, false, content)); + } + if let Some(dot_pos) = trimmed.find(". ") { + if dot_pos > 0 && trimmed[0..dot_pos].chars().all(|c| c.is_numeric()) { + let content = trimmed[dot_pos + 2..].trim().to_string(); + log(&format!("Ordered list item found: '{}'", content)); + return Some((indent, true, content)); + } + } + None +} + +fn is_horizontal_rule(line: &str) -> bool { + let trimmed = line.trim(); + if trimmed.len() < 3 { + return false; + } + (trimmed.starts_with("---") && trimmed.chars().all(|c| c == '-' || c.is_whitespace())) || + (trimmed.starts_with("***") && trimmed.chars().all(|c| c == '*' || c.is_whitespace())) || + (trimmed.starts_with("___") && trimmed.chars().all(|c| c == '_' || c.is_whitespace())) +} + +fn process_table(lines: &[&str]) -> String { + log(&format!("Processing table with {} rows", lines.len())); + let mut html = String::from("\n"); + for (i, line) in lines.iter().enumerate() { + if !line.trim().starts_with('|') { + continue; + } + let cells: Vec<&str> = line.split('|') + .filter(|s| !s.trim().is_empty()) + .map(|s| s.trim()) + .collect(); + if cells.is_empty() { + continue; + } + if i == 0 || line.contains("---") { + html.push_str(""); + for cell in &cells { + html.push_str(&format!("", process_inline(cell, false))); + } + html.push_str("\n"); + } else { + html.push_str(""); + for cell in &cells { + html.push_str(&format!("", process_inline(cell, false))); + } + html.push_str("\n"); + } + } + html.push_str("
{}
{}
\n"); + html +} + +fn process_code_block(lines: &[&str], start_idx: &mut usize) -> String { + log("Code block detected"); + let mut html = String::from("
");
+    *start_idx += 1;
+    while *start_idx < lines.len() && !lines[*start_idx].starts_with("```") {
+        html.push_str(lines[*start_idx]);
+        html.push('\n');
+        *start_idx += 1;
+    }
+    html.push_str("
\n"); + *start_idx += 1; + html +} + +fn process_list(lines: &[&str], start_idx: &mut usize) -> String { + log("Starting new list"); + let mut html = String::from("\n"); + html +} + +fn md_to_ansi(md_content: &str) -> String { + let mut output = String::new(); + let lines: Vec<&str> = md_content.lines().collect(); + let mut i = 0; + while i < lines.len() { + let line = lines[i]; + if line.trim().starts_with('|') && i + 1 < lines.len() && lines[i + 1].contains("---") { + let mut table_lines = Vec::new(); + while i < lines.len() && lines[i].trim().starts_with('|') { + table_lines.push(lines[i]); + i += 1; + } + process_table_ansi(&mut output, &table_lines); + continue; + } + if let Some(h) = process_heading(line, true) { + output.push_str(&h); + output.push('\n'); + i += 1; + continue; + } + let trimmed = line.trim_start(); + if trimmed.starts_with("- ") || trimmed.starts_with("* ") || trimmed.starts_with("+ ") { + let content = trimmed[2..].trim(); + output.push_str(&format!(" • {}\n", process_inline(content, true))); + i += 1; + continue; + } + let processed = process_inline(line, true); + if !processed.trim().is_empty() { + output.push_str(&processed); + output.push('\n'); + } else if line.trim().is_empty() { + output.push('\n'); + } + i += 1; + } + output +} + +fn md_to_html(md_content: &str) -> String { + log("Starting Markdown to HTML conversion"); + let mut html = String::from(r#" + + + + + Markdown Preview + + + +"#); + let lines: Vec<&str> = md_content.lines().collect(); + let mut i = 0; + while i < lines.len() { + let line = lines[i]; + if is_horizontal_rule(line) { + html.push_str("
\n"); + i += 1; + continue; + } + if let Some(heading) = process_heading(line, false) { + html.push_str(&heading); + html.push('\n'); + i += 1; + continue; + } + if is_list_item(line).is_some() { + let list_html = process_list(&lines, &mut i); + html.push_str(&list_html); + continue; + } + if line.trim().starts_with('|') && i + 1 < lines.len() && lines[i + 1].contains("---") { + let mut table_lines = Vec::new(); + while i < lines.len() && lines[i].trim().starts_with('|') { + table_lines.push(lines[i]); + i += 1; + } + html.push_str(&process_table(&table_lines)); + continue; + } + if line.starts_with("```") { + let code_html = process_code_block(&lines, &mut i); + html.push_str(&code_html); + continue; + } + let processed = process_inline(line, false); + if !processed.trim().is_empty() { + html.push_str(&processed); + html.push('\n'); + } else if line.trim().is_empty() { + html.push_str("
\n"); + } + i += 1; + } + html.push_str(""); + html +} + +pub fn md_html_main(md_content: &str, output: &mut String) { + let html = md_to_html(md_content); + output.clear(); + output.push_str(&html); +} + +pub fn md_ansi_main(md_content: &str, output: &mut String) { + let ansi = md_to_ansi(md_content); + output.clear(); + output.push_str(&ansi); +} + diff --git a/src/col.rs b/src/col.rs new file mode 100755 index 0000000..2823781 --- /dev/null +++ b/src/col.rs @@ -0,0 +1,51 @@ +#[derive(Clone)] +pub struct HighlightConfig { + pub color_keyword: &'static str, + pub color_string: &'static str, + pub color_comment: &'static str, + pub color_header: &'static str, + pub color_tag: &'static str, + pub color_numbers: &'static str, + pub color_reset: &'static str, + pub color_ip: &'static str, + pub color_timestamp: &'static str, + pub color_hex: &'static str, + pub new_line: &'static str +} + +impl HighlightConfig { + pub fn html_colors() -> Self { + Self { + color_keyword: "", + color_string: "", + color_comment: "", + color_header: "", + color_tag: "", + color_numbers: "", + color_reset: "", + color_hex: "", + color_ip: "", + color_timestamp: "", + new_line: "
", + } + } +} + +impl Default for HighlightConfig { + fn default() -> Self { + Self { + color_keyword: "\x1b[38;2;0;255;255m", // Neon-Cyan + color_string: "\x1b[38;2;255;255;85m", // Neon-Gelb + color_comment: "\x1b[38;2;180;180;180m", // Helles Grau + color_header: "\x1b[38;2;255;0;255m", // Knalliges Magenta + color_tag: "\x1b[38;2;255;85;85m", // Neon-Rot + color_numbers: "\x1b[38;2;255;0;0m", // Rot + color_reset: "\x1b[0m", // Reset + color_ip: "\x1b[38;2;0;170;255m", // Leuchtendes Blau + color_timestamp: "\x1b[38;2;200;100;255m", // Lila/Violett + color_hex: "\x1b[38;2;220;220;220m", // Helles Silber + new_line: "\n", + } + } +} + diff --git a/detect_filetype.rs b/src/detect_filetype.rs old mode 100644 new mode 100755 similarity index 100% rename from detect_filetype.rs rename to src/detect_filetype.rs diff --git a/src/highlighter.rs b/src/highlighter.rs new file mode 100755 index 0000000..5eb18b9 --- /dev/null +++ b/src/highlighter.rs @@ -0,0 +1,278 @@ +use std::fs; + +use crate::keywords::{ + ASM_8051_KEYWORDS, ASM_X86_KEYWORDS, KEYWORDS_C, DOCKER_KEYWORDS, JS_KEYWORDS, + PYTHON_KEYWORDS, RUST_KEYWORDS, SHELL_KEYWORDS, SQL_KEYWORDS, Keyword, + DOCKER_COMPOSE_KEYWORDS, JAVA_KEYWORDS +}; + +use crate::state::State; +use crate::language::Language; +use crate::col::HighlightConfig; + +fn is_word_char(c: char) -> bool { + c.is_alphanumeric() || c == '_' || c == '$' +} + +fn is_keyword(word: &str, keywords: &[Keyword]) -> bool +{ + keywords.iter().any(|kw| kw.0 == word) +} + +pub fn append_line_number(state: &mut State, result: &mut String, con: &HighlightConfig) +{ + if !state.flag_nr { + return; + } + if state.line_nr < 10 { + result.push_str(&format!("{}{} {}", con.color_numbers, state.line_nr, con.color_reset)); + } else if state.line_nr < 100 { + result.push_str(&format!("{}{} {}", con.color_numbers, state.line_nr, con.color_reset)); + } else { + result.push_str(&format!("{}{} {}", con.color_numbers, state.line_nr, con.color_reset)); + } +} + +pub fn highlight_line(line: &str, lang: Language, config: &HighlightConfig, result: &mut String, state: &mut State) -> bool +{ + if state.stop_at_line <= state.line_nr { + return true; + } + state.line_nr += 1; + append_line_number(state, result, config); + let mut p = line.chars().peekable(); + while let Some(mut ch) = p.next() { + match lang { + Language::AsmX86 | Language::Asm8051 => { + if ch == ';' { + result.push_str(config.color_comment); + result.push(';'); + for c in p { result.push(c); } + result.push_str(config.color_reset); + break; + } + } + Language::Python | Language::Shell => { + if ch == '#' { + result.push_str(config.color_comment); + result.push('#'); + for c in p { result.push(c); } + result.push_str(config.color_reset); + break; + } + } + Language::JavaScript | Language::Docker => { + if ch == '/' && p.peek() == Some(&'/') { + result.push_str(config.color_comment); + result.push_str("//"); + p.next(); + for c in p { + result.push(c); + } + result.push_str(config.color_reset); + break; + } + } + Language::Clang => { + if ch == '/' { + match p.peek() { + Some(&'/') => { + result.push_str(config.color_comment); + result.push_str("//"); + p.next(); + for c in p { + result.push(c); + } + result.push_str(config.color_reset); + break; + } + Some(&'*') => { + result.push_str(config.color_comment); + result.push_str("/*"); + p.next(); + while let Some(c) = p.next() { + if c == '*' && p.peek() == Some(&'/') { + result.push('*'); + result.push('/'); + p.next(); + result.push_str(config.color_reset); + break; + } + result.push(c); + } + continue; + } + _ => {} + } + } + } + Language::Sql => { + if ch == '/' && p.peek() == Some(&'*') { + result.push_str(config.color_comment); + result.push_str("/*"); + p.next(); + while let Some(c) = p.next() { + if c == '*' && p.peek() == Some(&'/') { + result.push('*'); + result.push('/'); + p.next(); + result.push_str(config.color_reset); + break; + } + result.push(c); + } + continue; + } + } + _ => {} + } + if ch == '"' || ch == '\'' { + let quote = ch; + result.push_str(config.color_string); + result.push(quote); + ch = match p.next() { + Some(c) => c, + None => { result.push_str(config.color_reset); break; } + }; + while ch != quote { + if ch == '\\' { + result.push('\\'); + if let Some(next) = p.next() { + result.push(next); + ch = match p.next() { Some(c) => c, None => break }; + continue; + } + } else { + result.push(ch); + } + ch = match p.next() { Some(c) => c, None => break }; + } + if ch == quote { + result.push(quote); + } + result.push_str(config.color_reset); + continue; + } + if lang == Language::Html && ch == '<' { + result.push_str(config.color_tag); + result.push('<'); + ch = match p.next() { Some(c) => c, None => { result.push_str(config.color_reset); break; } }; + while ch != '>' { + if ch == '"' { + let quote = ch; + result.push_str(config.color_string); + result.push(quote); + ch = match p.next() { Some(c) => c, None => break }; + while ch != quote { + if ch == '\\' { + result.push('\\'); + if let Some(n) = p.next() { result.push(n); ch = match p.next() { Some(c)=>c, None=>break }; continue; } + } else { + result.push(ch); + } + ch = match p.next() { Some(c) => c, None => break }; + } + if ch == quote { + result.push(quote); + } + result.push_str(config.color_tag); + } else { + result.push(ch); + } + ch = match p.next() { Some(c) => c, None => break }; + } + if ch == '>' { + result.push('>'); + } + result.push_str(config.color_reset); + continue; + } + if is_word_char(ch) { + let mut buf = String::new(); + buf.push(ch); + while let Some(&next) = p.peek() { + if is_word_char(next) && buf.len() < 250 { + buf.push(next); + p.next(); + } else { + break; + } + } + let is_kw = match lang { + Language::Python => is_keyword(&buf, PYTHON_KEYWORDS), + Language::JavaScript => is_keyword(&buf, JS_KEYWORDS), + Language::Clang => is_keyword(&buf, KEYWORDS_C), + Language::Shell => is_keyword(&buf, SHELL_KEYWORDS), + Language::Docker => is_keyword(&buf, DOCKER_KEYWORDS), + Language::AsmX86 => is_keyword(&buf, ASM_X86_KEYWORDS), + Language::Asm8051 => is_keyword(&buf, ASM_8051_KEYWORDS), + Language::Sql => is_keyword(&buf, SQL_KEYWORDS), + Language::Rust => is_keyword(&buf, RUST_KEYWORDS), + Language::LangDockerCompose => is_keyword(&buf, DOCKER_COMPOSE_KEYWORDS), + Language::LangJava => is_keyword(&buf, JAVA_KEYWORDS), + _ => false, + }; + if is_kw { + result.push_str(config.color_keyword); + result.push_str(&buf); + result.push_str(config.color_reset); + } else { + result.push_str(&buf); + } + continue; + } + result.push(ch); + } + result.push_str(config.new_line); + return false; +} + +pub fn print_binary_file(filepath: &str, output: &mut String, state: &mut State, config: &HighlightConfig) { + let bytes = match fs::read(filepath) { + Ok(b) => b, + Err(e) => { + output.push_str(&format!("Fehler beim Lesen der Datei: {}\n", e)); + return; + } + }; + if bytes.is_empty() { + output.push_str("Datei ist leer.\n"); + return; + } + let mut line_nr: u64 = 0; + for (i, chunk) in bytes.chunks(8).enumerate() { + output.push_str(config.color_header); + output.push_str(&format!("{:08x} ", i * 8)); + output.push_str(config.color_reset); + if state.flag_nr == true { + output.push_str(config.color_string); + output.push_str(&format!("{:08} ", line_nr)); + output.push_str(config.color_reset); + } + output.push_str(config.color_keyword); + let new_line_char: &u8 = &0xA; + for byte in chunk { + if byte == new_line_char { + line_nr += 1; + } + output.push_str(&format!("{:02x} ", byte)); + } + output.push_str(config.color_reset); + output.push_str(config.color_comment); + for _ in chunk.len()..8 { + output.push_str(" "); + } + output.push_str(config.color_reset); + output.push_str(" |"); + for &byte in chunk { + if (32..=126).contains(&byte) { + output.push(byte as char); + } else { + output.push('.'); + } + } + output.push_str("|\n"); + } + output.push_str(&format!("\n{} Bytes insgesamt.\n", bytes.len())); +} + diff --git a/src/keywords.rs b/src/keywords.rs new file mode 100755 index 0000000..60a32c3 --- /dev/null +++ b/src/keywords.rs @@ -0,0 +1,141 @@ +#[derive(Clone, Copy, PartialEq)] +pub struct Keyword(pub &'static str); + +pub static DOCKER_COMPOSE_KEYWORDS: &[Keyword] = &[ + Keyword("version"), Keyword("name"), Keyword("services"), + Keyword("volumes"), Keyword("configs"), Keyword("networks"), + Keyword("secrets"), Keyword("include"), Keyword("image"), Keyword("build"), + Keyword("context"), Keyword("dockerfile"), Keyword("args"), + Keyword("command"), Keyword("entrypoint"), Keyword("ports"), Keyword("expose"), + Keyword("environment"), Keyword("env_file"), Keyword("depends_on"), + Keyword("restart"), Keyword("deploy"), Keyword("labels"), Keyword("healthcheck"), + Keyword("working_dir"), Keyword("user"), Keyword("privileged"), Keyword("cap_add"), + Keyword("cap_drop"), Keyword("devices"), Keyword("dns"), Keyword("dns_search"), + Keyword("tmpfs"), Keyword("ulimits"), Keyword("stdin_open"), Keyword("tty"), + Keyword("profiles"), Keyword("scale"), Keyword("init"), Keyword("pid"), Keyword("ipc"), + Keyword("isolation"), Keyword("cpu_count"), Keyword("cpu_percent"), Keyword("cpu_shares"), + Keyword("cpu_quota"), Keyword("cpus"), Keyword("mem_limit"), Keyword("mem_reservation"), + Keyword("memswap_limit"), Keyword("shm_size"), Keyword("oom_score_adj"), Keyword("target"), + Keyword("cache_from"), Keyword("cache_to"), Keyword("extra_hosts"), Keyword("logging"), + Keyword("security_opt"), Keyword("stop_grace_period"), Keyword("stop_signal"), + Keyword("sysctls"), Keyword("platform"), Keyword("driver"), Keyword("driver_opts"), + Keyword("external"), Keyword("ipam"), Keyword("attachable"), Keyword("internal"), + Keyword("ipv6"), Keyword("file"), Keyword("content"), +]; + +pub static RUST_KEYWORDS: &[Keyword] = &[ + Keyword("fn"), Keyword("let"), Keyword("mut"), Keyword("match"), Keyword("if"), + Keyword("else"), Keyword("loop"), Keyword("while"), Keyword("for"), Keyword("in"), + Keyword("impl"), Keyword("struct"), Keyword("enum"), Keyword("trait"), Keyword("pub"), + Keyword("use"), Keyword("mod"), Keyword("where"), Keyword("type"), Keyword("move"), + Keyword("async"), Keyword("await"), Keyword("return"), Keyword("break"), + Keyword("continue"), Keyword("const"), Keyword("static"), Keyword("unsafe"), + Keyword("dyn"), Keyword("as"), Keyword("ref"), Keyword("true"), Keyword("false"), + Keyword("self"), Keyword("Self"), Keyword("crate"), Keyword("super"), +]; + +pub static SQL_KEYWORDS: &[Keyword] = &[ + Keyword("SELECT"), Keyword("FROM"), Keyword("WHERE"), Keyword("INSERT"), + Keyword("INTO"), Keyword("VALUES"), Keyword("UPDATE"), Keyword("SET"), + Keyword("DELETE"), Keyword("CREATE"), Keyword("TABLE"), Keyword("DROP"), + Keyword("ALTER"), Keyword("ADD"), Keyword("JOIN"), Keyword("INNER"), + Keyword("LEFT"), Keyword("RIGHT"), Keyword("FULL"), Keyword("ON"), + Keyword("GROUP"), Keyword("BY"), Keyword("ORDER"), Keyword("ASC"), + Keyword("DESC"), Keyword("DISTINCT"), Keyword("LIMIT"), Keyword("OFFSET"), + Keyword("UNION"), Keyword("ALL"), Keyword("AND"), Keyword("OR"), + Keyword("NOT"), Keyword("NULL"), Keyword("IS"), Keyword("IN"), + Keyword("LIKE"), Keyword("BETWEEN"), Keyword("EXISTS"), Keyword("CASE"), + Keyword("WHEN"), Keyword("THEN"), Keyword("ELSE"), Keyword("END"), + Keyword("PRIMARY"), Keyword("KEY"), Keyword("FOREIGN"), Keyword("REFERENCES"), + Keyword("INDEX"), Keyword("VIEW"), Keyword("AS"), Keyword("HAVING"), +]; + +pub static ASM_X86_KEYWORDS: &[Keyword] = &[ + Keyword("mov"), Keyword("add"), Keyword("adc"), Keyword("sub"), Keyword("sbb"), + Keyword("inc"), Keyword("dec"), Keyword("mul"), Keyword("imul"), Keyword("div"), + Keyword("idiv"), Keyword("and"), Keyword("or"), Keyword("xor"), Keyword("not"), + Keyword("neg"), Keyword("shl"), Keyword("shr"), Keyword("sal"), Keyword("sar"), + Keyword("rol"), Keyword("ror"), Keyword("rcl"), Keyword("rcr"), Keyword("cmp"), + Keyword("test"), Keyword("push"), Keyword("pop"), Keyword("pushf"), Keyword("popf"), + Keyword("pusha"), Keyword("popa"), Keyword("lea"), Keyword("xchg"), + Keyword("movsx"), Keyword("movzx"), Keyword("bswap"), Keyword("jmp"), Keyword("je"), + Keyword("jne"), Keyword("jg"), Keyword("jl"), Keyword("jge"), Keyword("jle"), + Keyword("ja"), Keyword("jb"), Keyword("jae"), Keyword("jbe"), Keyword("call"), + Keyword("ret"), Keyword("int"), Keyword("iret"), Keyword("loop"), Keyword("loope"), + Keyword("loopne"), Keyword("rep"), Keyword("repe"), Keyword("repne"), + Keyword("stos"), Keyword("lods"), Keyword("movs"), Keyword("cmps"), Keyword("scas"), + Keyword("cli"), Keyword("sti"), Keyword("hlt"), Keyword("nop"), + Keyword("syscall"), Keyword("sysret"), Keyword("movaps"), Keyword("addps"), + Keyword("mulps"), Keyword("movsd"), Keyword("addsd"), Keyword("mulsd"), + Keyword("vmovdqa64"), Keyword("vpcmpneqq"), Keyword("kmovd"), Keyword("vzeroupper"), + Keyword("vmovdqu"), Keyword("vmovdqa"), Keyword("vmovmskpd"), Keyword("vpmovmskb"), + Keyword("vmovdqu8"), Keyword("vpcmpneqb"), Keyword("kmovq"), Keyword("vpcmpeqq"), + Keyword("vpcmpeqb"), Keyword("jz") +]; + +pub static ASM_8051_KEYWORDS: &[Keyword] = &[ + Keyword("NOP"), Keyword("AJMP"), Keyword("LJMP"), Keyword("SJMP"), Keyword("JMP"), + Keyword("ACALL"), Keyword("LCALL"), Keyword("RET"), Keyword("RETI"), Keyword("RR"), + Keyword("RL"), Keyword("RRC"), Keyword("RLC"), Keyword("INC"), Keyword("DEC"), + Keyword("ADD"), Keyword("ADDC"), Keyword("SUBB"), Keyword("DA"), Keyword("ANL"), + Keyword("ORL"), Keyword("XRL"), Keyword("CLR"), Keyword("CPL"), Keyword("MOV"), + Keyword("MOVC"), Keyword("MOVX"), Keyword("XCH"), Keyword("PUSH"), Keyword("POP"), + Keyword("SWAP"), Keyword("MUL"), Keyword("DIV"), Keyword("JB"), Keyword("JNB"), + Keyword("JC"), Keyword("JNC"), Keyword("JZ"), Keyword("JNZ"), Keyword("JBC"), + Keyword("DJNZ"), Keyword("CJNE"), +]; + +pub static PYTHON_KEYWORDS: &[Keyword] = &[ + Keyword("def"), Keyword("class"), Keyword("import"), Keyword("from"), Keyword("for"), + Keyword("while"), Keyword("if"), Keyword("elif"), Keyword("else"), Keyword("return"), + Keyword("True"), Keyword("False"), Keyword("None"), Keyword("with"), Keyword("as"), + Keyword("try"), Keyword("except"), Keyword("finally"), Keyword("in"), Keyword("is"), + Keyword("not"), +]; + +pub static JS_KEYWORDS: &[Keyword] = &[ + Keyword("function"), Keyword("const"), Keyword("let"), Keyword("var"), Keyword("if"), + Keyword("else"), Keyword("for"), Keyword("while"), Keyword("return"), Keyword("class"), + Keyword("import"), Keyword("from"), Keyword("export"), Keyword("true"), Keyword("false"), + Keyword("null"), Keyword("undefined"), Keyword("new"), Keyword("this"), +]; + +pub static KEYWORDS_C: &[Keyword] = &[ + Keyword("int"), Keyword("char"), Keyword("float"), Keyword("double"), Keyword("void"), + Keyword("return"), Keyword("if"), Keyword("else"), Keyword("for"), Keyword("while"), + Keyword("struct"), Keyword("typedef"), Keyword("enum"), Keyword("const"), + Keyword("static"), Keyword("unsigned"), Keyword("signed"), Keyword("long"), + Keyword("short"), Keyword("include"), Keyword("define"), Keyword("ifdef"), Keyword("endif"), + Keyword("pragma"), Keyword("error"), Keyword("do"), Keyword("ifndef"), + Keyword("size_t"), Keyword("ssize_t"), Keyword("undef"), + Keyword("defined"), Keyword("ifndef"), Keyword("elif"), Keyword("goto") +]; + +pub static SHELL_KEYWORDS: &[Keyword] = &[ + Keyword("if"), Keyword("then"), Keyword("else"), Keyword("fi"), Keyword("for"), + Keyword("in"), Keyword("do"), Keyword("done"), Keyword("case"), Keyword("esac"), + Keyword("while"), Keyword("function"), Keyword("elif"), +]; + +pub static DOCKER_KEYWORDS: &[Keyword] = &[ + Keyword("FROM"), Keyword("RUN"), Keyword("CMD"), Keyword("ENTRYPOINT"), Keyword("COPY"), + Keyword("ADD"), Keyword("ENV"), Keyword("ARG"), Keyword("WORKDIR"), Keyword("EXPOSE"), + Keyword("USER"), Keyword("VOLUME"), Keyword("LABEL"), Keyword("ONBUILD"), +]; + +pub static JAVA_KEYWORDS: &[Keyword] = &[ + Keyword("abstract"), Keyword("assert"), Keyword("boolean"), Keyword("break"), Keyword("byte"), + Keyword("case"), Keyword("catch"), Keyword("char"), Keyword("class"), Keyword("const"), + Keyword("continue"), Keyword("default"), Keyword("do"), Keyword("double"), Keyword("else"), + Keyword("enum"), Keyword("extends"), Keyword("final"), Keyword("finally"), Keyword("float"), + Keyword("for"), Keyword("goto"), Keyword("if"), Keyword("implements"), Keyword("import"), + Keyword("instanceof"), Keyword("int"), Keyword("interface"), Keyword("long"), Keyword("native"), + Keyword("new"), Keyword("package"), Keyword("private"), Keyword("protected"), Keyword("public"), + Keyword("return"), Keyword("short"), Keyword("static"), Keyword("strictfp"), Keyword("super"), + Keyword("switch"), Keyword("synchronized"), Keyword("this"), Keyword("throw"), Keyword("throws"), + Keyword("transient"), Keyword("try"), Keyword("void"), Keyword("volatile"), Keyword("while"), + Keyword("true"), Keyword("false"), Keyword("null"), + Keyword("var"), Keyword("yield"), Keyword("record"), Keyword("sealed"), Keyword("permits"), + Keyword("non-sealed"), Keyword("when"), +]; + diff --git a/language.rs b/src/language.rs old mode 100644 new mode 100755 similarity index 100% rename from language.rs rename to src/language.rs diff --git a/main.rs b/src/main.rs old mode 100644 new mode 100755 similarity index 100% rename from main.rs rename to src/main.rs diff --git a/src/specials.rs b/src/specials.rs new file mode 100755 index 0000000..ce454a4 --- /dev/null +++ b/src/specials.rs @@ -0,0 +1,212 @@ +use crate::state::State; +use crate::col::HighlightConfig; + +const BINARY_LINE_LENGTH: usize = 48; +const HEADER_LENGTH_BYTE: u32 = 38; + +pub fn print_hex_file(filepath: &str, state: &mut State, config: &HighlightConfig) { + let mut file: std::fs::File = match std::fs::File::open(filepath) { + Ok(opened_file) => opened_file, + Err(_) => { + println!("Fehler beim Lesen der Datei: {}", filepath); + return; + } + }; + let file_size: u64 = match file.metadata() { + Ok(metadata) => metadata.len(), + Err(_) => { + println!("Fehler beim Lesen der Datei: {}", filepath); + return; + } + }; + if file_size == 0 { + println!("Datei ist leer."); + return; + } + let file_size_usize: usize = file_size as usize; + let mut bytes: Vec = Vec::with_capacity(file_size_usize); + bytes.resize(file_size_usize, 0u8); + match std::io::Read::read_exact(&mut file, &mut bytes) { + Ok(()) => {} + Err(_) => { + println!("Fehler beim Lesen der Datei: {}", filepath); + return; + } + } + let bytes_read: usize = bytes.len(); + let mut chunk_offset: usize = 0; + while chunk_offset < bytes_read { + let mut current_chunk_len: usize = bytes_read - chunk_offset; + if current_chunk_len > BINARY_LINE_LENGTH { + current_chunk_len = BINARY_LINE_LENGTH; + } + print!( + "{}{:08x} {}", + config.color_header, + chunk_offset, + config.color_reset + ); + if state.flag_nr { + print!("{}{:08} {}", config.color_string, state.line_nr, config.color_reset); + } + print!("{}", config.color_keyword); + let mut byte_index: usize = 0; + while byte_index < current_chunk_len { + let current_byte: u8 = bytes[chunk_offset + byte_index]; + if current_byte == b'\n' { + state.line_nr += 1; + } + print!("{:02x} ", current_byte); + byte_index += 1; + } + print!("{}", config.color_reset); + if current_chunk_len < 8 { + print!("{}", config.color_comment); + let mut pad_index: usize = current_chunk_len; + while pad_index < BINARY_LINE_LENGTH { + print!(" "); + pad_index += 1; + } + print!("{}", config.color_reset); + } + print!(" |"); + byte_index = 0; + while byte_index < current_chunk_len { + let current_byte: u8 = bytes[chunk_offset + byte_index]; + if current_byte >= 32 && current_byte <= 126 { + print!("{}", current_byte as char); + } else { + print!("."); + } + byte_index += 1; + } + println!("|"); + chunk_offset += current_chunk_len; + } + println!("{}{}{}Bytes insgesamt.", config.new_line, bytes_read, config.new_line); +} + +pub fn log_highlight_line(line: &str, state: &mut State, config: &HighlightConfig) { + let line_bytes: &[u8] = line.as_bytes(); + let mut index: usize = 0; + let mut stage: i32 = 0; + print_line_number(state, config); + state.line_nr += 1; + let mut char_counter: u32 = 0; + while index < line_bytes.len() { + let current_byte: u8 = line_bytes[index]; + if current_byte == b'[' && char_counter < HEADER_LENGTH_BYTE { + if stage == 0 { + print!("{}[", config.color_timestamp); + index += 1; + while index < line_bytes.len() && line_bytes[index] != b']' { + print!("{}", line_bytes[index] as char); + index += 1; + } + if index < line_bytes.len() && line_bytes[index] == b']' { + print!("]{}", config.color_reset); + index += 1; + stage += 1; + } + } else { + print!("{}[", config.color_ip); + index += 1; + while index < line_bytes.len() && line_bytes[index] != b']' { + print!("{}", line_bytes[index] as char); + index += 1; + } + if index < line_bytes.len() && line_bytes[index] == b']' { + print!("]{}", config.color_reset); + index += 1; + stage = 0; + } + } + continue; + } else if current_byte == b'0' + && index + 3 < line_bytes.len() + && line_bytes[index + 1] == b'x' + { + print!("{}0x", config.color_hex); + index += 2; + print!("{}", line_bytes[index] as char); + index += 1; + print!("{} ", line_bytes[index] as char); + index += 1; + print!("{}", config.color_reset); + if index < line_bytes.len() && line_bytes[index] == b' ' { + index += 1; + } + continue; + } else { + print!("{}", current_byte as char); + index += 1; + } + char_counter += 1; + } + print!("{}", config.new_line); +} + +pub fn to_c_array(filename: &str, config: &HighlightConfig) { + let mut input_file: std::fs::File = match std::fs::File::open(filename) { + Ok(opened_file) => opened_file, + Err(_) => { + eprintln!("Fehler: Datei {} konnte nicht geöffnet werden", filename); + return; + } + }; + let file_size_u64: u64 = match input_file.metadata() { + Ok(metadata) => metadata.len(), + Err(_) => { + eprintln!("Fehler: ftell fehlgeschlagen"); + return; + } + }; + let file_size: usize = file_size_u64 as usize; + let mut data_buffer: Vec = Vec::with_capacity(file_size); + data_buffer.resize(file_size, 0u8); + if file_size > 0 { + match std::io::Read::read_exact(&mut input_file, &mut data_buffer) { + Ok(()) => {} + Err(_) => { + eprintln!("Fehler: Datei konnte nicht vollständig gelesen werden"); + return; + } + } + } + println!("{}const unsigned char BUFF[1822][4] = {{", config.color_keyword); + let mut index: usize = 0; + while index < file_size { + if index % 16 == 0 { + print!(" "); + } + print!("{}0x{:08X}{}", config.color_numbers, data_buffer[index], config.color_reset); + if index + 1 < file_size { + print!(", "); + } + if (index + 1) % 16 == 0 { + println!(""); + } + if index > (16 * 1822) { + return; + } + index += 1; + } + println!(""); + println!("{}}};{}", config.color_keyword, config.color_reset); +} + +fn print_line_number(state: &State, config: &HighlightConfig) { + if !state.flag_nr { + return; + } + let line_nr = state.line_nr; + let formatted_line = if line_nr < 10 { + format!("{}{} {}", config.color_header, line_nr, config.color_reset) + } else if line_nr < 100 { + format!("{}{} {}", config.color_header, line_nr, config.color_reset) + } else { + format!("{}{} {}", config.color_header, line_nr, config.color_reset) + }; + print!("{}", formatted_line); +} + diff --git a/state.rs b/src/state.rs old mode 100644 new mode 100755 similarity index 100% rename from state.rs rename to src/state.rs