add base build system

This commit is contained in:
2025-04-13 18:27:06 +02:00
parent 66df99954e
commit ea1da4abd0
24 changed files with 596 additions and 52 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/dst/

62
Makefile Normal file
View File

@@ -0,0 +1,62 @@
EMIT_ASSETS := index.html 404.html info.html \
styles.css icons.css \
countdown.js theme.js theme.js \
stallman.webp stallman_2024.webp
SEARCH_REPLACE := lib/search_and_replace/target/release/search_and_replace
COMPONENTS := head.html header.html nav.html footer.html
.PHONY: build
build: $(EMIT_ASSETS:%=dst/cz/%) $(EMIT_ASSETS:%=dst/en/%) dst/favicon.ico
.PHONY: clean
clean:
rm -rf dst
$(SEARCH_REPLACE): $(shell find lib/search_and_replace/src)
cd lib/search_and_replace && cargo build --release
define make_page
cat $(1) | \
$(SEARCH_REPLACE) \
'<!--#include virtual="components/head.html" -->' "$$(cat components/$(3)/head.html)" \
'<!--#include virtual="components/header.html" -->' "$$(cat components/$(3)/header.html)" \
'<!--#include virtual="components/nav.html" -->' "$$(cat components/$(3)/nav.html)" \
'<!--#include virtual="components/footer.html" -->' "$$(cat components/$(3)/footer.html)" \
> $(2)
endef
dst/cz/%.html: cz_%.html $(SEARCH_REPLACE) $(COMPONENTS:%=components/cz/%)
@mkdir -p $(@D)
$(call make_page,$<,$@,cz)
dst/cz/%.css: %.css
@mkdir -p $(@D)
ln -f $< $@
dst/cz/%.js: js/%.js
@mkdir -p $(@D)
ln -f $< $@
dst/cz/%.webp: images/%.jpg
@mkdir -p $(@D)
magick $< $@
dst/en/%.html: en_%.html $(SEARCH_REPLACE) $(COMPONENTS:%=components/en/%)
@mkdir -p $(@D)
$(call make_page,$<,$@,en)
dst/en/%.css: %.css
@mkdir -p $(@D)
ln -f $< $@
dst/en/%.js: js/%.js
@mkdir -p $(@D)
ln -f $< $@
dst/en/%.webp: images/%.jpg
@mkdir -p $(@D)
magick $< $@
dst/favicon.ico: images/favicon.ico
@mkdir -p $(@D)
ln -f $< $@

View File

@@ -1,5 +1,3 @@
<footer> <footer>
<!--#set var="URL" value="https://${HTTP_HOST}${DOCUMENT_URI}"-->
<p>Tato webová stránka podléhá licenci GNU Free Documentation License v1.3.</p> <p>Tato webová stránka podléhá licenci GNU Free Documentation License v1.3.</p>
</footer> </footer>

View File

@@ -4,5 +4,4 @@
<link rel="icon" type="image/png" href="favicon.png"> <link rel="icon" type="image/png" href="favicon.png">
<link href="icons.css" rel="stylesheet"> <link href="icons.css" rel="stylesheet">
<link href="styles.css" rel="stylesheet"> <link href="styles.css" rel="stylesheet">
<script src="js/theme.js"></script> <script src="theme.js"></script>

View File

@@ -4,4 +4,3 @@
<img src="data:," alt="Přepínač světlého a tmavého režimu" class="light-dark-switch" onclick="switch_theme();"> <img src="data:," alt="Přepínač světlého a tmavého režimu" class="light-dark-switch" onclick="switch_theme();">
<img src="data:," alt="Přepínač širokého a úzkého rozvržení" class="layout-switch" onclick="switch_layout();"> <img src="data:," alt="Přepínač širokého a úzkého rozvržení" class="layout-switch" onclick="switch_layout();">
</header> </header>

View File

@@ -6,4 +6,3 @@
<li><a href="javascript">JavaScript</a></li> <li><a href="javascript">JavaScript</a></li>
</ul> </ul>
</nav> </nav>

View File

@@ -0,0 +1,3 @@
<footer>
<p>This website falls under the GNU Free Documentation License v1.3.</p>
</footer>

7
components/en/head.html Normal file
View File

@@ -0,0 +1,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="darkred">
<link rel="icon" type="image/png" href="favicon.png">
<link href="icons.css" rel="stylesheet">
<link href="styles.css" rel="stylesheet">
<script src="theme.js"></script>

View File

@@ -0,0 +1,6 @@
<header>
<div class="header-title">Software Libre Liberec</div>
<div class="header-subtitle">Richard Stallman at Technical University of Liberec</div>
<img src="data:," alt="Přepínač světlého a tmavého režimu" class="light-dark-switch" onclick="switch_theme();">
<img src="data:," alt="Přepínač širokého a úzkého rozvržení" class="layout-switch" onclick="switch_layout();">
</header>

8
components/en/nav.html Normal file
View File

@@ -0,0 +1,8 @@
<nav>
<ul>
<li><a href=".">Homepage</a></li>
<li><a href="stallman">Richard Stallman</a></li>
<li><a href="free">Software Libre</a></li>
<li><a href="javascript">JavaScript</a></li>
</ul>
</nav>

View File

@@ -15,4 +15,3 @@
<!--#include virtual="components/footer.html" --> <!--#include virtual="components/footer.html" -->
</body> </body>
</html> </html>

View File

@@ -17,7 +17,7 @@
</div> </div>
<a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc23.jpg"> <a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc23.jpg">
<figure> <figure>
<img class="countdown-bg" src="images/stallman_2024.jpg" alt="Pan Stallman v Itálii"> <img class="countdown-bg" src="stallman_2024.webp" alt="Pan Stallman v Itálii">
<figcaption>Snímek z přednášky pana Stallmana z roku 2024</figcaption> <figcaption>Snímek z přednášky pana Stallmana z roku 2024</figcaption>
</figure> </figure>
</a> </a>
@@ -42,8 +42,7 @@
a Free Software Foundation. a Free Software Foundation.
</p> </p>
</main> </main>
<script src="js/countdown.js"></script> <script src="countdown.js"></script>
<!--#include virtual="components/footer.html" --> <!--#include virtual="components/footer.html" -->
</body> </body>
</html> </html>

View File

@@ -4,27 +4,17 @@
<!--#include virtual="components/head.html" --> <!--#include virtual="components/head.html" -->
<title>Richard Stallman - Svobodný software Liberec</title> <title>Richard Stallman - Svobodný software Liberec</title>
<meta name="description" content="Krátký životopis pana Stallmana."> <meta name="description" content="Krátký životopis pana Stallmana.">
<script src="js/lang.js"></script> <script src="lang.js"></script>
</head> </head>
<body> <body>
<!--#include virtual="components/header.html" --> <!--#include virtual="components/header.html" -->
<!--#include virtual="components/nav.html" --> <!--#include virtual="components/nav.html" -->
<main> <main>
<p>
Tato stránka obsahuje překlad textu v angličtině,
který nám poskytl sám pan Stallman.
</p>
<p>
<a href="javascript:change_lang('#stallman_bio');">Kliknutím zde</a>
si zobrazíte původní znění textu (či znovu překlad po opakovaném kliknutí).
</p>
<h1>Kdo je Richard Stallman?</h1> <h1>Kdo je Richard Stallman?</h1>
<a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc1.jpg"> <a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc1.jpg">
<figure class="aside"> <figure class="aside">
<img src="images/stallman.jpg" alt="Richard Stallman"> <img src="stallman.webp" alt="Richard Stallman">
<figcaption>Richard Stallman, 2024</figcaption> <figcaption>Richard Stallman, 2024</figcaption>
</figure> </figure>
</a> </a>
@@ -55,35 +45,7 @@
Zakladatel a vedoucí vývoje <a href="https://www.gnu.org/">operačního systému GNU</a> Zakladatel a vedoucí vývoje <a href="https://www.gnu.org/">operačního systému GNU</a>
</p> </p>
</div> </div>
<div id="stallman_bio_en" style="display: none">
<p>
Dr. Richard Stallman launched the free software movement in 1983 and
started the development of the GNU operating system
(see <a href="https://www.gnu.org">www.gnu.org</a>) in 1984.
GNU is free software: everyone has the freedom to copy it
and redistribute it, with or without changes. The GNU/Linux system,
basically the GNU operating system with <a href="https://kernel.org/">Linux</a>
as the kernel, is used on tens of millions of computers today.
</p>
<p>
Stallman has received the ACM Grace
Hopper Award and the ACM Software and Systems Award, a MacArthur
Foundation fellowship, the Electronic Frontier Foundation's Pioneer
Award, and the the Takeda Award for Social/Economic Betterment, as
well as many doctorates honoris causa, and has been inducted into the
<a href="https://www.internethalloffame.org/">Internet Hall of Fame</a>.
</p>
<p>
Dr Richard Stallman<br>
Founder of the <a href="https://www.fsf.org/">Free Software Foundation</a><br>
Founder and development leader of the <a href="https://www.gnu.org/">GNU operating system</a>
</p>
</div>
</main> </main>
<!--#include virtual="components/footer.html" --> <!--#include virtual="components/footer.html" -->
</body> </body>
</html> </html>

17
en_404.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="cs">
<head>
<!--#include virtual="components/head.html" -->
<title>Page not found - Software Libre Liberec</title>
</head>
<body>
<!--#include virtual="components/header.html" -->
<!--#include virtual="components/nav.html" -->
<main style="text-align: center">
<h1>Page not found.</h1>
<p>You have attempted to load a page that does not exist on this server.</p>
<p>Please click on one of the links above.</p>
</main>
<!--#include virtual="components/footer.html" -->
</body>
</html>

48
en_index.html Normal file
View File

@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="cs">
<head>
<!--#include virtual="components/head.html" -->
<title>Svobodný software Liberec</title>
<meta name="description" content="Richard Stallman at Technical University of Liberec.">
</head>
<body>
<!--#include virtual="components/header.html" -->
<!--#include virtual="components/nav.html" -->
<main>
<h1>Richard Stallman on free software</h1>
<div class="img-text-container">
<div class="img-text">
<div class="countdown">--:--:--:--</div>
</div>
<a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc23.jpg">
<figure>
<img class="countdown-bg" src="stallman_2024.webp" alt="Pan Stallman v Itálii">
<figcaption>Photograph from Mr Stallman's lecture in 2024</figcaption>
</figure>
</a>
</div>
<p>
The event will happen on the <b>7th of may from 16:30 to 19:30</b> in the auditorium of building G at Technical University of Liberec.
</p>
<p>
The main part of the event will be a lecture by Mr. Richard Stallman on free software &ndash;
a movement that Mr. Stallman himself founded in 1983.
It will focus on the dangers of the constant proliferation of non-free software in our digital society,
how such software limits us as users, and how we can fight against it with free software.
</p>
<p>
This event is organized by the Faculty of Mechatronics, Informatics and Interdisciplinary Studies
of the Technical University of Liberec. Like most of Mr. Stallman's lectures, this event
is intended for the general public. There will be a voluntary admission fee only,
after the lecture it will also be possible to purchase items with the theme of the GNU operating system
and the Free Software Foundation.
</p>
</main>
<script src="countdown.js"></script>
<!--#include virtual="components/footer.html" -->
</body>
</html>

51
en_info.html Normal file
View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="cs">
<head>
<!--#include virtual="components/head.html" -->
<title>Richard Stallman - Software Libre Liberec</title>
<meta name="description" content="Short bio of mister Stallman.">
<script src="lang.js"></script>
</head>
<body>
<!--#include virtual="components/header.html" -->
<!--#include virtual="components/nav.html" -->
<main>
<h1>Who is Richard Stallman?</h1>
<a href="https://commons.wikimedia.org/wiki/File:Richard_Stallman_Bologna_2024_abc1.jpg">
<figure class="aside">
<img src="stallman.webp" alt="Richard Stallman">
<figcaption>Richard Stallman, 2024</figcaption>
</figure>
</a>
<div id="stallman_bio_en">
<p>
Dr. Richard Stallman launched the free software movement in 1983 and
started the development of the GNU operating system
(see <a href="https://www.gnu.org">www.gnu.org</a>) in 1984.
GNU is free software: everyone has the freedom to copy it
and redistribute it, with or without changes. The GNU/Linux system,
basically the GNU operating system with <a href="https://kernel.org/">Linux</a>
as the kernel, is used on tens of millions of computers today.
</p>
<p>
Stallman has received the ACM Grace
Hopper Award and the ACM Software and Systems Award, a MacArthur
Foundation fellowship, the Electronic Frontier Foundation's Pioneer
Award, and the the Takeda Award for Social/Economic Betterment, as
well as many doctorates honoris causa, and has been inducted into the
<a href="https://www.internethalloffame.org/">Internet Hall of Fame</a>.
</p>
<p>
Dr Richard Stallman<br>
Founder of the <a href="https://www.fsf.org/">Free Software Foundation</a><br>
Founder and development leader of the <a href="https://www.gnu.org/">GNU operating system</a>
</p>
</div>
</main>
<!--#include virtual="components/footer.html" -->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 B

View File

@@ -129,4 +129,3 @@
<!--#include virtual="components/footer.html" --> <!--#include virtual="components/footer.html" -->
</body> </body>
</html> </html>

BIN
images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

View File

@@ -54,4 +54,3 @@
<!--#include virtual="components/footer.html" --> <!--#include virtual="components/footer.html" -->
</body> </body>
</html> </html>

1
lib/search_and_replace/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
target/

7
lib/search_and_replace/Cargo.lock generated Normal file
View File

@@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "search_and_replace"
version = "0.1.0"

View File

@@ -0,0 +1,9 @@
[package]
name = "search_and_replace"
version = "0.1.0"
edition = "2021"
[profile.release]
strip = true
[dependencies]

View File

@@ -0,0 +1,371 @@
use std::{env::args, io::Read};
#[derive(Debug, PartialEq)]
struct JumpTable {
jumps: Vec<usize>,
}
impl JumpTable {
fn resolve_char(
pat: &Vec<char>,
ptr: &usize,
cur_char: &char,
) -> usize {
if pat.get(*ptr).unwrap() == cur_char {
return *ptr + 1;
}
if pat.get(0).unwrap() == cur_char {
return 1;
}
return 0;
}
pub fn new(pat: &Vec<char>) -> Self {
if pat.len() == 0 {
return Self { jumps: vec![] };
}
let mut ptr = 0_usize;
let mut jumps = vec![0];
for cur_char in pat.iter().skip(1) {
ptr = JumpTable::resolve_char(pat, &ptr, cur_char);
jumps.push(ptr);
}
Self { jumps }
}
pub fn search(&self, pat: &Vec<char>, needle: &char, ptr: &usize) -> usize {
let mut ptr = *ptr;
while ptr != 0 {
if pat.get(ptr).unwrap() == needle {
break;
}
ptr = *self.jumps.get(ptr.saturating_sub(1)).unwrap();
}
ptr
}
}
#[derive(Debug, PartialEq)]
struct Pattern {
pat: Vec<char>,
jt: JumpTable,
ptr: usize,
}
impl Pattern {
pub fn new(pat: String) -> Result<Self, ()> {
if pat.len() == 0 {
return Err(());
}
let pat = pat.chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
Ok(Self {
pat,
jt,
ptr: 0,
})
}
pub fn search_and_update(&mut self, cur_char: &char) -> bool {
if self.pat.get(self.ptr).unwrap() == cur_char {
self.ptr += 1;
return if self.ptr == self.pat.len() { true } else { false };
}
let found = self.jt.search(&self.pat, &cur_char, &self.ptr);
self.ptr = found;
false
}
pub fn reset_ptr(&mut self) {
self.ptr = 0;
}
pub fn len(&self) -> usize {
self.pat.len()
}
}
#[derive(Debug, PartialEq, Eq)]
struct Match {
pub pat_id: usize,
pub start_pos: usize,
pub len: usize,
}
impl Match {
pub fn new(pat_id: usize, start_pos: usize, len: usize) -> Self {
Self { pat_id, start_pos, len }
}
}
#[derive(Debug)]
struct MPSearch {
pats: Vec<Pattern>,
}
impl MPSearch {
pub fn new(pats: Vec<Pattern>) -> Self {
Self { pats }
}
fn reset_all_ptrs(&mut self) {
for pat in self.pats.iter_mut() {
pat.reset_ptr();
}
}
fn search_at_pos(&mut self, cur_char: &char, pos: &usize) -> Option<Match> {
let mut cur_match = None;
for (idx, pat) in self.pats.iter_mut().enumerate() {
if pat.search_and_update(cur_char) {
cur_match = Some(Match::new(idx, pos.saturating_sub(pat.len() - 1), pat.len()));
break;
}
}
if let Some(_) = cur_match {
self.reset_all_ptrs();
}
cur_match
}
pub fn search(&mut self, haystack: &String) -> Vec<Match> {
let mut res = Vec::new();
for (cur_idx, cur_char) in haystack.chars().enumerate() {
if let Some(cur_match) = self.search_at_pos(&cur_char, &cur_idx) {
res.push(cur_match);
}
}
res
}
}
macro_rules! skip_n {
($iter: ident, $n: expr) => {
for _ in 0..$n {
$iter.next().ok_or(())?;
}
}
}
macro_rules! append_n {
($iter: ident, $target: ident, $n: expr) => {
for _ in 0..$n {
$target.push($iter.next().ok_or(())?);
}
}
}
#[derive(Debug)]
struct MPReplace {
replacements: Vec<String>,
matches: Vec<Match>,
}
impl MPReplace {
pub fn new(matches: Vec<Match>, replacements: Vec<String>) -> Self {
Self { replacements, matches }
}
pub fn replace(&self, target: &String) -> Result<String, ()> {
let mut ptr = 0;
let mut iter = target.chars();
let mut res = String::new();
for cur_match in self.matches.iter() {
append_n!(iter, res, cur_match.start_pos - ptr);
skip_n!(iter, cur_match.len);
res.push_str(self.replacements.get(cur_match.pat_id).ok_or(())?);
ptr = cur_match.start_pos + cur_match.len;
}
append_n!(iter, res, target.chars().count() - ptr);
Ok(res)
}
}
struct PatMatchArgs<I: Iterator<Item = String>> {
inner: I,
}
impl<I: Iterator<Item = String>> PatMatchArgs<I> {
pub fn new(inner: I, count: usize) -> Result<Self, ()> {
if count % 2 != 0 {
return Err(());
}
Ok(Self { inner })
}
}
impl<I: Iterator<Item = String>> Iterator for PatMatchArgs<I> {
type Item = (String, String);
fn next(&mut self) -> Option<Self::Item> {
Some((self.inner.next()?, self.inner.next()?))
}
}
fn main() {
let mut contents = Vec::new();
std::io::stdin().lock().read_to_end(&mut contents).unwrap();
let contents = std::str::from_utf8(&contents).unwrap().to_string();
let args = match PatMatchArgs::new(args().skip(1), args().len() - 1) {
Ok(val) => val,
Err(()) => {
eprintln!("the number of arguments after filepath must be divisible by two");
std::process::exit(1);
}
};
let mut pats = Vec::new();
let mut replacements = Vec::new();
for (cur_pat, cur_replacement) in args {
let Ok(cur_pat) = Pattern::new(cur_pat) else {
eprintln!("the patterns can't be empty");
std::process::exit(1);
};
pats.push(cur_pat);
replacements.push(cur_replacement);
}
let matches = MPSearch::new(pats).search(&contents);
let replaced = MPReplace::new(matches, replacements)
.replace(&contents).unwrap();
println!("{}", replaced);
}
#[cfg(test)]
mod test {
use crate::{JumpTable, MPReplace, MPSearch, Match, Pattern};
#[test]
fn jumps_01() {
let src = String::from("thisthen").chars().collect::<Vec<_>>();
let target = vec![0_usize, 0, 0, 0, 1, 2, 0, 0];
let jt = JumpTable::new(&src);
assert_eq!(jt.jumps, target);
}
#[test]
fn jumps_02() {
let src = String::from("tthis").chars().collect::<Vec<_>>();
let target = vec![0, 1, 0, 0, 0];
let jt = JumpTable::new(&src);
assert_eq!(jt.jumps, target);
}
#[test]
fn jumps_03() {
let src = String::from("t").chars().collect::<Vec<_>>();
let target = vec![0];
let jt = JumpTable::new(&src);
assert_eq!(jt.jumps, target);
}
#[test]
fn jumps_04() {
let src = String::from("tt").chars().collect::<Vec<_>>();
let target = vec![0, 1];
let jt = JumpTable::new(&src);
assert_eq!(jt.jumps, target);
}
#[test]
fn jumps_05() {
let src = String::from("").chars().collect::<Vec<_>>();
let target = vec![];
let jt = JumpTable::new(&src);
assert_eq!(jt.jumps, target);
}
#[test]
fn search_01() {
let pat = String::from("tthis").chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
assert_eq!(jt.search(&pat, &'t', &1), 1);
}
#[test]
fn search_02() {
let pat = String::from("testtesa").chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
assert_eq!(jt.search(&pat, &'t', &7), 3);
}
#[test]
fn search_03() {
let pat = String::from("ahojahoj").chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
assert_eq!(jt.search(&pat, &'j', &7), 7);
}
#[test]
fn search_04() {
let pat = String::from("ahojahoj").chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
assert_eq!(jt.search(&pat, &'j', &7), 7);
}
#[test]
fn search_05() {
let pat = String::from("jojojojojojoja").chars().collect::<Vec<_>>();
let jt = JumpTable::new(&pat);
assert_eq!(jt.search(&pat, &'o', &13), 11);
}
#[test]
fn search_and_update_01() {
let pat = String::from("test");
let mut pat = Pattern::new(pat).unwrap();
let haystack = String::from("thisisatest");
for cur_haystack in haystack.chars().take(haystack.len() - 1) {
assert_eq!(pat.search_and_update(&cur_haystack), false);
println!("{:?}", pat);
}
println!("{:?}", pat);
assert_eq!(pat.search_and_update(&'t'), true);
}
#[test]
fn empty_pattern() {
assert_eq!(Pattern::new("".chars().collect()), Err(()));
}
#[test]
fn mpsearch_01() {
let mut mpsearch = MPSearch::new(
vec!["this", "is", "a", "test"]
.iter()
.map(|p| Pattern::new(p.to_string()).unwrap())
.collect()
);
println!("{:?}", mpsearch);
let haystack = String::from("this is a test");
let target = vec![
Match::new(0, 0, 4),
Match::new(1, 5, 2),
Match::new(2, 8, 1),
Match::new(3, 10, 4)
];
assert_eq!(mpsearch.search(&haystack), target);
}
#[test]
fn mpreplace_01() {
let mut mpsearch = MPSearch::new(
vec!["this", "is", "a", "test"]
.iter()
.map(|p| Pattern::new(p.to_string()).unwrap())
.collect()
);
let haystack = String::from("this-is.a*test///");
let matches = mpsearch.search(&haystack);
let mpreplace = MPReplace::new(matches, vec![
"that".to_string(),
"isn't".to_string(),
"the".to_string(),
"final".to_string(),
]);
let replaced = mpreplace.replace(&haystack).unwrap();
assert_eq!(replaced, "that-isn't.the*final///");
}
}