148 lines
5.9 KiB
Rust
148 lines
5.9 KiB
Rust
//! # mharkup
|
|
//!
|
|
//! another markup language
|
|
//!
|
|
//! ## sections
|
|
//!
|
|
//! Sections are delimited by double newlines (\n\n).
|
|
//! A double newline can be included in any section by surrounding
|
|
//! the section with at least one set of square brackets.
|
|
//!
|
|
//! ## input
|
|
//!
|
|
//! Must be utf-8 and must end with a linebreak.
|
|
//! Linebreaks are \n (no \r).
|
|
|
|
use std::{
|
|
io::{Read, Write},
|
|
net::Shutdown,
|
|
time::{Duration, SystemTime},
|
|
};
|
|
|
|
use crate::{doc::Document, parse::parse};
|
|
|
|
mod doc;
|
|
mod parse;
|
|
mod to;
|
|
|
|
fn main() {
|
|
let args = std::env::args().skip(1).collect::<Vec<_>>();
|
|
if args.len() < 2 {
|
|
eprintln!("Usage: <filename>.mharkup <format> [specs...]");
|
|
eprintln!("output formats and (available specs)");
|
|
eprintln!("` html: a static html+css document (html, texm)");
|
|
eprintln!("` html-http: html with auto-refresh");
|
|
return;
|
|
}
|
|
let specs = &args[2..];
|
|
let src = || std::fs::read_to_string(&args[0]).unwrap();
|
|
match args[1].to_lowercase().trim() {
|
|
"html" => println!(
|
|
"{}",
|
|
to::html::page(&parse(&src()).unwrap(), specs, &mut to::html::State::new())
|
|
),
|
|
"html-http" => {
|
|
let addr = std::env::var("ADDR");
|
|
let addr = addr.as_ref().map_or("localhost:8000", |v| v.as_str());
|
|
let wait = std::env::var("WAIT")
|
|
.ok()
|
|
.and_then(|v| v.trim().parse().ok())
|
|
.unwrap_or(100);
|
|
eprintln!();
|
|
eprintln!("Binding to {addr} (ADDR env var)");
|
|
eprintln!("Checking file mtime every {wait}ms while handling a request (WAIT env var)");
|
|
let listener = std::net::TcpListener::bind(addr).unwrap();
|
|
eprintln!();
|
|
eprintln!("With your browser, open http://{addr}/");
|
|
eprintln!("Without javascript, use http://{addr}/s");
|
|
eprintln!("For a static page, open http://{addr}/1");
|
|
let mut ptime = SystemTime::UNIX_EPOCH + Duration::from_mins(9);
|
|
loop {
|
|
let mut state = to::html::State::new();
|
|
let mut buf1 = vec![String::new(); 1024];
|
|
let mut buf2 = vec![None; 1024];
|
|
let mut i = 0;
|
|
// connection might time out after 30 seconds,
|
|
// so send a 304 before that can happen.
|
|
let maxchecks = 25 * 1000 / wait;
|
|
'accept_connection: while i < 1024 {
|
|
let (mut connection, _) = listener.accept().unwrap();
|
|
let mut buf = [0u8; 8];
|
|
if connection.read_exact(&mut buf).is_err() {
|
|
continue;
|
|
}
|
|
let allow_delay = if let Ok(req) = str::from_utf8(&buf) {
|
|
if req.contains(" /u") {
|
|
state.head_to_body = "</head>\n<body>";
|
|
true
|
|
} else if req.contains(" /1") {
|
|
state.head_to_body = "</head>\n<body>";
|
|
false
|
|
} else if req.contains(" /s") {
|
|
state.head_to_body =
|
|
"<meta http-equiv=\"refresh\" content=\"1\">\n</head>\n<body>";
|
|
true
|
|
} else {
|
|
state.head_to_body = r#"<script>
|
|
const BODY_REGEX = new RegExp("<bo"+"dy>.*<\\/bo"+"dy>", "misv");
|
|
(async () => { while (true) {
|
|
let res = await fetch("/u");
|
|
if (!res.ok) continue;
|
|
let str = (await res.text()).match(BODY_REGEX)[0];
|
|
document.body.outerHTML = str;
|
|
} })();
|
|
</script>
|
|
</head>
|
|
<body>"#;
|
|
false
|
|
}
|
|
} else {
|
|
continue;
|
|
};
|
|
if allow_delay {
|
|
'delay_connection: {
|
|
for _ in 0..maxchecks {
|
|
let mtime =
|
|
std::fs::metadata(&args[0]).unwrap().modified().unwrap();
|
|
if mtime != ptime {
|
|
ptime = mtime;
|
|
break 'delay_connection;
|
|
} else {
|
|
std::thread::sleep(std::time::Duration::from_millis(wait));
|
|
}
|
|
}
|
|
connection
|
|
.write_all(
|
|
b"HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n",
|
|
)
|
|
.ok();
|
|
connection.shutdown(Shutdown::Both).ok();
|
|
continue 'accept_connection;
|
|
}
|
|
}
|
|
buf1[i] = src();
|
|
let src = unsafe { &*((&buf1[i]) as *const String) };
|
|
buf2[i] = Some(match parse(src) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
eprintln!("{}", e.display(src));
|
|
continue;
|
|
}
|
|
});
|
|
let doc = unsafe { &*(buf2[i].as_ref().unwrap() as *const Document<'_>) };
|
|
i += 1;
|
|
let mut http =
|
|
"HTTP/1.1 200 Ok\r\nContent-Length: 0000000000\r\n\r\n".to_owned();
|
|
let len = http.len();
|
|
to::html::page_to(doc, specs, &mut state, &mut http);
|
|
let content_length = format!("{}", http.len() - len);
|
|
http.replace_range(len - 4 - content_length.len()..len - 4, &content_length);
|
|
connection.write_all(http.as_bytes()).ok();
|
|
connection.shutdown(Shutdown::Both).ok();
|
|
}
|
|
}
|
|
}
|
|
_ => eprintln!("Unknown format, run without arguments for help"),
|
|
}
|
|
}
|