From ea1da4abd0e64bf522e8605effa737f467eb57e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mekina?= Date: Sun, 13 Apr 2025 18:27:06 +0200 Subject: [PATCH] add base build system --- .gitignore | 1 + Makefile | 62 +++++ components/{ => cz}/footer.html | 2 - components/{ => cz}/head.html | 3 +- components/{ => cz}/header.html | 1 - components/{ => cz}/nav.html | 1 - components/en/footer.html | 3 + components/en/head.html | 7 + components/en/header.html | 6 + components/en/nav.html | 8 + 404.html => cz_404.html | 1 - index.html => cz_index.html | 5 +- stallman.html => cz_info.html | 42 +--- en_404.html | 17 ++ en_index.html | 48 ++++ en_info.html | 51 ++++ favicon.png | Bin 185 -> 0 bytes free.html | 1 - images/favicon.ico | Bin 0 -> 198 bytes javascript.html | 1 - lib/search_and_replace/.gitignore | 1 + lib/search_and_replace/Cargo.lock | 7 + lib/search_and_replace/Cargo.toml | 9 + lib/search_and_replace/src/main.rs | 371 +++++++++++++++++++++++++++++ 24 files changed, 596 insertions(+), 52 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile rename components/{ => cz}/footer.html (60%) rename components/{ => cz}/head.html (88%) rename components/{ => cz}/header.html (99%) rename components/{ => cz}/nav.html (99%) create mode 100644 components/en/footer.html create mode 100644 components/en/head.html create mode 100644 components/en/header.html create mode 100644 components/en/nav.html rename 404.html => cz_404.html (99%) rename index.html => cz_index.html (92%) rename stallman.html => cz_info.html (54%) create mode 100644 en_404.html create mode 100644 en_index.html create mode 100644 en_info.html delete mode 100644 favicon.png create mode 100644 images/favicon.ico create mode 100644 lib/search_and_replace/.gitignore create mode 100644 lib/search_and_replace/Cargo.lock create mode 100644 lib/search_and_replace/Cargo.toml create mode 100644 lib/search_and_replace/src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f106fd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/dst/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4b63d7c --- /dev/null +++ b/Makefile @@ -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) \ + '' "$$(cat components/$(3)/head.html)" \ + '' "$$(cat components/$(3)/header.html)" \ + '' "$$(cat components/$(3)/nav.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 $< $@ diff --git a/components/footer.html b/components/cz/footer.html similarity index 60% rename from components/footer.html rename to components/cz/footer.html index f4450f9..c603285 100644 --- a/components/footer.html +++ b/components/cz/footer.html @@ -1,5 +1,3 @@ - diff --git a/components/head.html b/components/cz/head.html similarity index 88% rename from components/head.html rename to components/cz/head.html index 4d5b9ce..16cf667 100644 --- a/components/head.html +++ b/components/cz/head.html @@ -4,5 +4,4 @@ - - + diff --git a/components/header.html b/components/cz/header.html similarity index 99% rename from components/header.html rename to components/cz/header.html index 34d201a..8daad3e 100644 --- a/components/header.html +++ b/components/cz/header.html @@ -4,4 +4,3 @@ Přepínač světlého a tmavého režimu Přepínač širokého a úzkého rozvržení - diff --git a/components/nav.html b/components/cz/nav.html similarity index 99% rename from components/nav.html rename to components/cz/nav.html index 2031e81..6ecb7b1 100644 --- a/components/nav.html +++ b/components/cz/nav.html @@ -6,4 +6,3 @@
  • JavaScript
  • - diff --git a/components/en/footer.html b/components/en/footer.html new file mode 100644 index 0000000..03a4c8e --- /dev/null +++ b/components/en/footer.html @@ -0,0 +1,3 @@ + diff --git a/components/en/head.html b/components/en/head.html new file mode 100644 index 0000000..16cf667 --- /dev/null +++ b/components/en/head.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/components/en/header.html b/components/en/header.html new file mode 100644 index 0000000..6d8b35f --- /dev/null +++ b/components/en/header.html @@ -0,0 +1,6 @@ +
    +
    Software Libre Liberec
    +
    Richard Stallman at Technical University of Liberec
    + Přepínač světlého a tmavého režimu + Přepínač širokého a úzkého rozvržení +
    diff --git a/components/en/nav.html b/components/en/nav.html new file mode 100644 index 0000000..55a1f7f --- /dev/null +++ b/components/en/nav.html @@ -0,0 +1,8 @@ + diff --git a/404.html b/cz_404.html similarity index 99% rename from 404.html rename to cz_404.html index b859994..89c4ae4 100644 --- a/404.html +++ b/cz_404.html @@ -15,4 +15,3 @@ - diff --git a/index.html b/cz_index.html similarity index 92% rename from index.html rename to cz_index.html index 4d3c31b..1f5ad83 100644 --- a/index.html +++ b/cz_index.html @@ -17,7 +17,7 @@
    - Pan Stallman v Itálii + Pan Stallman v Itálii
    Snímek z přednášky pana Stallmana z roku 2024
    @@ -42,8 +42,7 @@ a Free Software Foundation.

    - + - diff --git a/stallman.html b/cz_info.html similarity index 54% rename from stallman.html rename to cz_info.html index 0b43abf..a8e7474 100644 --- a/stallman.html +++ b/cz_info.html @@ -4,27 +4,17 @@ Richard Stallman - Svobodný software Liberec - +
    -

    - Tato stránka obsahuje překlad textu v angličtině, - který nám poskytl sám pan Stallman. -

    - -

    - Kliknutím zde - si zobrazíte původní znění textu (či znovu překlad po opakovaném kliknutí). -

    -

    Kdo je Richard Stallman?

    - Richard Stallman + Richard Stallman
    Richard Stallman, 2024
    @@ -55,35 +45,7 @@ Zakladatel a vedoucí vývoje operačního systému GNU

    - -
    - diff --git a/en_404.html b/en_404.html new file mode 100644 index 0000000..65bdca2 --- /dev/null +++ b/en_404.html @@ -0,0 +1,17 @@ + + + + + Page not found - Software Libre Liberec + + + + +
    +

    Page not found.

    +

    You have attempted to load a page that does not exist on this server.

    +

    Please click on one of the links above.

    +
    + + + diff --git a/en_index.html b/en_index.html new file mode 100644 index 0000000..8d33b41 --- /dev/null +++ b/en_index.html @@ -0,0 +1,48 @@ + + + + + Svobodný software Liberec + + + + + +
    +

    Richard Stallman on free software

    + + + +

    + The event will happen on the 7th of may from 16:30 to 19:30 in the auditorium of building G at Technical University of Liberec. +

    + +

    + The main part of the event will be a lecture by Mr. Richard Stallman on free software – + 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. +

    + +

    + 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. +

    +
    + + + + diff --git a/en_info.html b/en_info.html new file mode 100644 index 0000000..bbf7bc3 --- /dev/null +++ b/en_info.html @@ -0,0 +1,51 @@ + + + + + Richard Stallman - Software Libre Liberec + + + + + + +
    +

    Who is Richard Stallman?

    + + +
    + Richard Stallman +
    Richard Stallman, 2024
    +
    +
    + +
    +

    + Dr. Richard Stallman launched the free software movement in 1983 and + started the development of the GNU operating system + (see www.gnu.org) 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 Linux + as the kernel, is used on tens of millions of computers today. +

    + +

    + 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 + Internet Hall of Fame. +

    + +

    + Dr Richard Stallman
    + Founder of the Free Software Foundation
    + Founder and development leader of the GNU operating system +

    +
    +
    + + + diff --git a/favicon.png b/favicon.png deleted file mode 100644 index b445347c465f4397d4711785e721f2abbfde4daf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 185 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#VfCf%EHFQ&TI@!Gx!eic%~#*`hQY0&`Mfk ze6V%7L(Hk83h9F4Q?|(LFcq({R<7i%IL>v?MQBHO52L)g{WIlOt>5c2`Y(t{gdVgH fII#GS{V`!-{}^tADz%wFyBR!P{an^LB{Ts5I`KR8 diff --git a/free.html b/free.html index af9df83..6549ed5 100644 --- a/free.html +++ b/free.html @@ -129,4 +129,3 @@ - diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1a3619a8cfd312df7c5dc2ba0ddad5f54a19e124 GIT binary patch literal 198 zcmZQzU<5(|0VXiLfq{WR42U&=SOAC>fEXl?5B&fCp8<%#_+A17!`<}=%nD?~ulWz#t8@3?_%qd}8DPOk*1% literal 0 HcmV?d00001 diff --git a/javascript.html b/javascript.html index 7ca5c15..43c49e8 100644 --- a/javascript.html +++ b/javascript.html @@ -54,4 +54,3 @@ - diff --git a/lib/search_and_replace/.gitignore b/lib/search_and_replace/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/lib/search_and_replace/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/lib/search_and_replace/Cargo.lock b/lib/search_and_replace/Cargo.lock new file mode 100644 index 0000000..81a53e1 --- /dev/null +++ b/lib/search_and_replace/Cargo.lock @@ -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" diff --git a/lib/search_and_replace/Cargo.toml b/lib/search_and_replace/Cargo.toml new file mode 100644 index 0000000..3125d2c --- /dev/null +++ b/lib/search_and_replace/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "search_and_replace" +version = "0.1.0" +edition = "2021" + +[profile.release] +strip = true + +[dependencies] diff --git a/lib/search_and_replace/src/main.rs b/lib/search_and_replace/src/main.rs new file mode 100644 index 0000000..f5a7df9 --- /dev/null +++ b/lib/search_and_replace/src/main.rs @@ -0,0 +1,371 @@ +use std::{env::args, io::Read}; + +#[derive(Debug, PartialEq)] +struct JumpTable { + jumps: Vec, +} + +impl JumpTable { + fn resolve_char( + pat: &Vec, + 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) -> 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, 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, + jt: JumpTable, + ptr: usize, +} + +impl Pattern { + pub fn new(pat: String) -> Result { + if pat.len() == 0 { + return Err(()); + } + let pat = pat.chars().collect::>(); + 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, +} + +impl MPSearch { + pub fn new(pats: Vec) -> 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 { + 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 { + 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, + matches: Vec, +} + +impl MPReplace { + pub fn new(matches: Vec, replacements: Vec) -> Self { + Self { replacements, matches } + } + + pub fn replace(&self, target: &String) -> Result { + 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> { + inner: I, +} + +impl> PatMatchArgs { + pub fn new(inner: I, count: usize) -> Result { + if count % 2 != 0 { + return Err(()); + } + Ok(Self { inner }) + } +} + +impl> Iterator for PatMatchArgs { + type Item = (String, String); + + fn next(&mut self) -> Option { + 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::>(); + 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::>(); + 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::>(); + 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::>(); + 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::>(); + let target = vec![]; + let jt = JumpTable::new(&src); + assert_eq!(jt.jumps, target); + } + + #[test] + fn search_01() { + let pat = String::from("tthis").chars().collect::>(); + let jt = JumpTable::new(&pat); + assert_eq!(jt.search(&pat, &'t', &1), 1); + } + + #[test] + fn search_02() { + let pat = String::from("testtesa").chars().collect::>(); + let jt = JumpTable::new(&pat); + assert_eq!(jt.search(&pat, &'t', &7), 3); + } + + #[test] + fn search_03() { + let pat = String::from("ahojahoj").chars().collect::>(); + let jt = JumpTable::new(&pat); + assert_eq!(jt.search(&pat, &'j', &7), 7); + } + + #[test] + fn search_04() { + let pat = String::from("ahojahoj").chars().collect::>(); + let jt = JumpTable::new(&pat); + assert_eq!(jt.search(&pat, &'j', &7), 7); + } + + #[test] + fn search_05() { + let pat = String::from("jojojojojojoja").chars().collect::>(); + 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///"); + } +}