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");
+ while *start_idx < lines.len() {
+ if let Some((_, _, content)) = is_list_item(lines[*start_idx]) {
+ html.push_str(&format!(" - {}
\n", process_inline(&content, false)));
+ *start_idx += 1;
+ } else if lines[*start_idx].trim().is_empty() {
+ *start_idx += 1;
+ } else {
+ break;
+ }
+ }
+ html.push_str("
\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