My personal website
git clone git://
Log | Files | Refs

index.html (24419B)

      1 <!DOCTYPE HTML>
      2 <html lan=en>
      3   <head>
      4     <title>index | Cem's Website</title>
      5     <meta charset="utf-8">
      6     <meta name="Description" content="Cem Keylan's Website">
      7     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      8     <style>
      9       html {font-family:monospace;font-size:16px;color:#282a36;}
     10       body {
     11 	  width: 90%;
     12 	  max-width: 1050px;
     13 	  margin-left: auto;
     14 	  margin-right: auto;
     15 	  margin-top: 20px;
     16 	  overflow: none;
     17           overflow-y: scroll;
     18 	  padding-right: 10px;
     19 	  padding-left: 10px;
     20       }
     21       a{text-decoration:none;font-weight:bold;color:#282a36;}
     22       a:hover{text-decoration:underline;}
     23       @media (prefers-color-scheme: dark) {
     24           html {color: white;background:#282a36;}
     25           a{color:white;}
     26       }
     27     </style>
     28     <link rel="stylesheet" href="/static/syntax.css">
     29     <script src="/static/highlight.pack.js"></script>
     30     <script>hljs.initHighlightingOnLoad();</script>
     31   </head>
     32   <body>
     33     <div class="header">
     34       <nav>
     35         <a href='/'>index</a> |
     36         <a href="/software.html">software</a> |
     37         <a href="/blog.html">blog</a> |
     38         <a href="/contact.html">contact</a> |
     39         <a href="/sysmgr">sysmgr</a> |
     40       </nav>
     41     </div>
     42     <hr>
     43     <p>
     44 <p>Hello! I am Cem, and this is my little blog that I rarely ever use. I am a
     45 Software Engineering student at the Berlin CODE university, the maintainer of
     46 Carbs Linux, and I occasionally compose music (mostly jazz and classical).</p>
     48 <p>My repositories are really scattered, so see the <a href="/software.html">software</a> section if you
     49 are looking for one of my works.</p>
     51 <p>This page is curl friendly! Just replace &lsquo;.html&rsquo; with &lsquo;.txt&rsquo;, and you will be
     52 able to view this site in your favourite pager! In your terminal simply type:</p>
     54 <pre><code>curl -sL | less
     55 </code></pre>
     57 <p><a href="/rss.xml">RSS Feed</a> | <a href="/blog.html">Blog Index</a></p>
     59 <hr />
     61 <h1>My thoughts on execline</h1>
     63 <p><a href="/blog/20211012-execline.html">Permalink</a></p>
     65 <p>Date: Oct 12 2021</p>
     67 <p>With the gaining popularity of the <a href="">s6-rc</a>, I have recently decided to check out
     68 <a href="">execline</a>. It is a shell-like scripting language that is built around chaining
     69 commands together. There is no interpreter, there is no &lsquo;built-in&rsquo; functionality
     70 even though the docs might make you think there are. Execline is best described
     71 as a suite of tools that imitate the functionality of a shell.</p>
     73 <p>There are a ton of information on the execline&rsquo;s page, especially discussing
     74 why skarnet prefers execline instead of <code>/bin/sh</code>. Those points are mostly
     75 valid, shells are a nightmare, and they suck at being compliant to the POSIX
     76 specification. What I don&rsquo;t agree with the <a href="">why not sh</a> page, however, is the
     77 part on performance. Even though execline does not have an interactive shell
     78 implementation of its own, it is still much slower than other shells simply by
     79 its design. Since the design of execline is built on process chaining, it
     80 requires spawning new processes for things as basic as variable declaration.
     81 Variable manipulation is the cheapest operation you would expect from a shell,
     82 but in execline, every operation costs the same regardless of how simple it is.</p>
     84 <p>Throughout my weeks of toying around with execline, I have came to the
     85 conclusion that execline is much better in simple scripts only. Execline is
     86 as usable as any shell out there, but even with its advantages over <code>sh</code>,
     87 execline is only better if it&rsquo;s simple. Execline is really good for certain
     88 specific situations such as service scripts (as used in s6-rc), or where you
     89 were already meant to chain a couple of commands together. After all, skarnet
     90 already presents these limitations on the website of execline.</p>
     92 <p>Execline can be leveraged as how s6-rc compiles service databases with other
     93 utilities, but I don&rsquo;t think it can be used as a shell replacement itself. It&rsquo;s
     94 not the next shiny thing to jump on to replace all your shell scripts with
     95 (unless you have really basic shell scripts). It does not have the flexibility
     96 nor the performance of the shell for scripts that can be considered a little
     97 over than the &ldquo;basic&rdquo;.</p>
     99 <hr />
    101 <h1>Reimplementing <code>sysmgr</code> in C</h1>
    103 <p><a href="/blog/20201002-reimplementing-sysmgr-in-c.html">Permalink</a></p>
    105 <p>Date: Oct 02 2020</p>
    107 <p>For a while, I have been thinking about implementing <a href="">sysmgr</a> in C. I started
    108 thinking about the inefficiencies of sysmgr. POSIX sh isn&rsquo;t particularly
    109 designed to have ultimate control over child processes. There are basic job
    110 management features that are <em>just enough</em> for sysmgr to do its job. The
    111 biggest pain is having to use tools like <code>sleep(1)</code> and <code>kill(1)</code>. Calling
    112 sleep every second, and using the kill command to check whether a process is
    113 alive or not is extremely inefficient. Some shells <em>do</em> include these commands
    114 built-in, but it isn&rsquo;t specified by POSIX, but one should never take this as
    115 granted.</p>
    117 <p>Lately, I have been adding C utilities to sysmgr to make it more efficient. This
    118 defeats the initial purpose of sysmgr, being a service manager in pure POSIX
    119 shell. My main purpose, however, is making sysmgr efficient and simplistic. It
    120 mostly imitates <code>runit</code> without dealing with all the complexity of the
    121 over-thinked <code>supervise</code> directory, nor the logging stuff. Most of these can be
    122 handled by the service script itself anyway. That&rsquo;s why instead of this ugly
    123 C/POSIX sh hybrid, I decided to implement it all in C.</p>
    125 <p>I am not a C expert or anything, I am learning a lot as I am writing the
    126 program. I want it to be C99 and portable (for BSD). It&rsquo;s currently not
    127 functional at all, but, you can see its current state <a href="">here</a>.</p>
    129 <p>EDIT Oct 10 2020:</p>
    131 <p>I did the initial release of this C version of sysmgr, which is more stable,
    132 and performant than the POSIX sh version. It still has rough edges, but is
    133 completely usable.</p>
    135 <hr />
    137 <h1>Trust in Distributed Environments</h1>
    139 <p><a href="/blog/20200908-trust-in-distributed-environments.html">Permalink</a></p>
    141 <p>Date: Sep 08 2020</p>
    143 <p>A few days ago, in the <code>#kisslinux</code> IRC channel, jedahan mentioned an
    144 implementation for trust in the package manager. While I was intrigued by the
    145 idea initially, I decided not to implement this for the upcoming 4.0.0 release.
    146 That is because the package manager and the distribution itself is already
    147 centered on trust. However, this idea made me think a lot about &ldquo;trust&rdquo; in
    148 distributed environments.</p>
    150 <p>Who and what would you trust? Would you trust Microsoft? Would you trust a
    151 binary? Would you only trust a so called &ldquo;reproducible&rdquo; build?</p>
    153 <p>Jedahan mentioned the possibility that a repository maintainer could create a
    154 package that would be normally in the distribution so they could mess your
    155 system up. He suggested a &ldquo;source&rdquo; system where you know where each package
    156 comes from. This way the package manager can warn you when the source of a
    157 package is changed. As I have said this idea intrigued me at the beginning, but
    158 here is why it is complex and unnecessary.</p>
    160 <p>The package manager would warn you every time you fork a package and apply your
    161 changes. Both with kiss and CPT, you already see git logs when the repositories
    162 are fetched. Those logs address each and every file that has been edited, added,
    163 removed, or renamed. CPT also has support for rsync, which is called verbosely.
    164 While not as descriptive, rsync also shows what&rsquo;s changed/added and what&rsquo;s
    165 deleted.</p>
    167 <p>Also, back on April, I have added submodule support to my fork of kiss, which
    168 Dylan adapted on May 19. I have added this feature because it solves a similar
    169 issue. I want to have only some packages from a repository and leave the rest
    170 of them. This way I am the one in control of what goes inside my repositories.</p>
    172 <p>Minor annoyances aside, would this solve the issue of trust? Maybe this evil
    173 repository maintainer decides to botch a package that was already in their
    174 repository not provided by your distribution. Should we then track the source
    175 files, build files as well? But those change all the time.</p>
    177 <p>I believe that this environment is as trustworthy as it can get, a repository
    178 system with package build instructions that easy to read and understand, easy to
    179 history check, easy to limit, and easy to allow. KISS and Carbs Linux have a
    180 single maintainer. I maintain Carbs and Dylan maintains KISS. You are not
    181 trusting an organization, you are trusting individuals that you can easily
    182 converse on the internet. The same goes for most community repository
    183 maintainers out there. Trying to implement more would be a &ldquo;security theater&rdquo;
    184 that would be a hassle for the maintainers, the users and the package manager
    185 without a noticeable benefit to any.</p>
    187 <hr />
    189 <h1>wpa_add script</h1>
    191 <p><a href="/blog/20200828-wpa-add-script.html">Permalink</a></p>
    193 <p>Date: Aug 28 2020</p>
    195 <p>I have this script named <code>wpa_add</code>, which I use to easily add new WiFi when I
    196 am outside, possibly in a cafe. I have written this script because I don&rsquo;t like
    197 the way my girlfriend looks at me while thinking that I am an absolute moron for
    198 not using Windows 10, and the entirety of Linux is a circlejerk. It is only
    199 natural that she thinks this way. I use my own distribution that doesn&rsquo;t have
    200 things like <code>dbus</code>, or <code>NetworkManager</code>, or one of those common desktop
    201 environments. You could install it by creating a simple package, but I am happy
    202 to not have any of those in my system.</p>
    204 <p>This script uses wpa-supplicant to add a new network and reconfigure. It uses
    205 dmenu for input, however you could replace dmenu calls with some command line
    206 prompts. I am doing the following assumptions:
    207 - You can manipulate <code>wpa_supplicant</code> without root access.
    208 - The configuration is on <code>/etc/wpa_supplicant.conf</code>.
    209 - You can edit <code>/etc/wpa/supplicant.conf</code>.</p>
    211 <p>If you want to ensure the above just do the following (as root):</p>
    213 <pre><code class="sh"># Add yourself to the wheel group if you aren't already.
    214 adduser user wheel
    216 # Change the ownership of /etc/wpa_supplicant.conf
    217 chown root:wheel /etc/wpa_supplicant.conf
    219 # Make sure the configuration can be edited by the wheel group.
    220 chmod 664 /etc/wpa_supplicant.conf
    221 </code></pre>
    223 <p>Your <code>wpa_supplicant</code> configuration must include the following line (or something similar):</p>
    225 <pre><code class="plaintext">ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
    226 </code></pre>
    228 <p>Here is the script</p>
    230 <pre><code class="sh">#!/bin/sh
    231 # Script to add wpa_supplicant networks through dmenu
    233 if [ "$1" ]; then
    234     name=$1
    235 else
    236     name=$(dmenu -p "Please enter network name, leave empty if you want to search" &lt;&amp;-)
    237 fi
    239 [ "$name" ] || {
    240     wpa_cli scan
    241     name=$(
    242     wpa_cli scan_results | sed 1,2d | while read -r _ _ _ _ ssid _; do
    243         # Hidden wifi are not to be returned
    244         [ "$ssid" ] || continue
    245         echo "$ssid"
    246     done | sort -u | dmenu -l 10 -p "Please choose WiFi")
    247     [ "$name" ] || exit 1
    248 }
    250 pass=$(dmenu -P -p "Please enter your password, leave empty if the network has open access.")
    252 if [ "$pass" ]; then
    253     wpa_passphrase "$name" &lt;&lt;EOF&gt;&gt; /etc/wpa_supplicant.conf
    254 $pass
    255 EOF
    256 else
    257     printf 'network={\n\tssid="%s"\n\tkey_mgmt=NONE\n\tpriority=-999\n}\n' "$name" &gt;&gt; /etc/wpa_supplicant.conf
    258 fi
    260 wpa_cli reconfigure
    261 </code></pre>
    263 <p>As I have said, you could do something similar in a command-line-only tool as
    264 well. This one uses <code>fzf</code> on WiFi selection.</p>
    266 <pre><code class="sh">#!/bin/sh -e
    268 stty="$(stty -g)"
    269 trap "stty $stty" EXIT INT TERM HUP
    271 if [ "$1" ]; then
    272     name=$1
    273 else
    274     printf 'Network Name, leave empty if you want to search: '
    275     read -r name
    276 fi
    278 [ "$name" ] || {
    279     wpa_cli scan &gt;/dev/null
    280     name=$(
    281     wpa_cli scan_results | sed 1,2d | while read -r _ _ _ _ ssid _; do
    282         # Hidden wifi are not to be returned
    283         [ "$ssid" ] || continue
    284         echo "$ssid"
    285     done | sort -u | fzf --prompt "Please choose WiFi: ")
    286 }
    288 [ "$name" ] || exit 1
    290 stty -echo
    291 printf 'Please enter your password, leave empty if the network has open access.\nPassword: '
    292 read -r pass
    294 if [ "$pass" ]; then
    295     wpa_passphrase "$name" &lt;&lt;EOF&gt;&gt; /etc/wpa_supplicant.conf
    296 $pass
    297 EOF
    298 else
    299     printf 'network={\n\tssid="%s"\n\tkey_mgmt=NONE\n\tpriority=-999\n}\n' "$name" &gt;&gt; /etc/wpa_supplicant.conf
    300 fi
    302 wpa_cli reconfigure
    303 </code></pre>
    305 <p>These scripts can be found as a gist <a href="">here</a></p>
    307 <hr />
    309 <h1>Static linking</h1>
    311 <p><a href="/blog/20200828-static-linking.html">Permalink</a></p>
    313 <p>Date: Aug 28 2020</p>
    315 <p>While I was working on a new initramfs generator for Carbs, I was once again
    316 reminded of the advantages of static linking software. Previously, I was using
    317 some really dumb script that was just basically using the package manager as a
    318 library for building the whole initramfs system from scratch. This system
    319 structure was completely statically linked, and the whole thing weighed around
    320 1.3MiB.</p>
    322 <p>Now, while <code>rd</code> (the small script that I had written) was good enough for me, it
    323 wouldn&rsquo;t be a fit to distribute with the system. It doesn&rsquo;t deal with dynamic
    324 binaries, kernel modules or library installation. So I have written this script
    325 that deals with those (kernel modules aren&rsquo;t done yet, though).</p>
    327 <p>The issue with build systems today are that the binaries are built dynamically
    328 unless you build the whole thing static. As long as there are shared libraries,
    329 the binaries will be dynamic as well. That&rsquo;s why the core repository of Carbs
    330 still contains dynamic binaries for gcc, binutils, util-linux and some other
    331 packages.</p>
    333 <p>The size of the new image with exactly the same binaries is a whopping 1.9MiB.
    334 While a size increase of 600KiB might not seem like a huge deal, I just want to
    335 tell you that busybox is static in both images, leaving ONLY TWO binaries that
    336 I install to my image; fsck and e2fsck. By switching from a static binary to
    337 dynamic + lib for only two programs, you require 600 KiB more space, and I have
    338 been talking about a gzip compressed cpio archive throughout this whole post.</p>
    340 <hr />
    342 <h1>Starting X without Xinit</h1>
    344 <p><a href="/blog/20200812-starting-x-without-xinit.html">Permalink</a></p>
    346 <p>Date: Aug 12 2020</p>
    348 <p>Most people who don&rsquo;t use a desktop environment use the <code>startx</code> command to
    349 initialize their X windowing system. Now, <code>startx</code> is a shell script that runs
    350 the C program <code>xinit</code> which basically runs <code>xorg-server</code>. Using xinit obviously
    351 has some nice perks. It makes some checks and runs your .xinitrc file. We don&rsquo;t
    352 need any of that though. Here is my X launcher:</p>
    354 <pre><code class="sh">#!/bin/sh
    356 export DISPLAY=${DISPLAY:-:0}
    357 trap "$HOME/.xinitrc" USR1
    359 (
    360     trap '' USR1
    362     exec X -keeptty :0 vt1
    363 ) &amp;
    365 wait
    366 </code></pre>
    368 <p>You need to keep in mind that your .xinitrc should be an executable.</p>
    370 <hr />
    372 <h1>Why I dislike Arch and Gentoo</h1>
    374 <p><a href="/blog/20200508-why-i-dislike-arch-and-gentoo.html">Permalink</a></p>
    376 <p>Date: May 08 2020</p>
    378 <p>Over the years, I have used many many Linux distributions. The reason I am
    379 now using a distribution maintained by me, is because I am never truly satisfied
    380 about other people&rsquo;s work. Not that they are bad at they do, it&rsquo;s just that
    381 they don&rsquo;t share the same vision as me. And I have felt this way with almost
    382 every distribution I have used.</p>
    384 <h2>Arch Linux</h2>
    386 <p>Arch Linux itself feels like it became a &lsquo;meme distribution&rsquo;. Their user-base
    387 is a cult-like community that think they are superior for using Arch Linux.
    388 Now, you might be an Arch user, and might not be like this. I used Arch for
    389 a long time, and didn&rsquo;t feel this way, ever. I only see this level of cultism
    390 for Arch and systemd.</p>
    392 <p>If you ever call Arch bloated on an online community website, you will get
    393 lots of crap for it. But in fact, Arch Linux is bloated. Now this isn&rsquo;t due
    394 to having too many packages in the base installation. This is because of their
    395 packaging of software.</p>
    397 <p>Arch enables almost every single option in the package configuration, meaning
    398 lots of unnecessary dependencies, and packages with humongous sizes.</p>
    400 <p>Pacman is a rather slow package manager, and missing alternatives. For me,
    401 an alternatives system is a must.</p>
    403 <p>If you want to use a better binary distribution, use Void Linux. They have
    404 saner package configurations, and the environment just feels more UNIXy. xbps
    405 is really fast, and has an alternatives system.</p>
    407 <h2>Gentoo Linux</h2>
    409 <p>This will be the longer part, because my dislike for Gentoo is bigger than
    410 my dislike towards Arch. If you want to see how NOT to maintain a distribution,
    411 check out Gentoo.</p>
    413 <p>I&rsquo;ve used Gentoo for a few months, and I&rsquo;m saying this right out of the
    414 gate. Portage is the slowest piece of software that I have ever used on
    415 Linux. Maybe that&rsquo;s because I deliberately avoid software using Python,
    416 but Portage is most probably the slowest package manager that is being
    417 used.</p>
    419 <p>Portage depends on Bash, Python, and GNU wget. I have got a line count from
    420 <code>cloc</code>, doing a <code>find . \( -name '*.sh -o -name '*.py' \) -exec cloc {} +</code>.
    421 The source code of just <code>*.sh</code> and <code>*.py</code> files are over 100k lines of code.
    422 Then I got curious and runned cloc against the whole repository. Here is
    423 the output.</p>
    425 <pre><code>--------------------------------------------------------------------------------
    426 Language                      files          blank        comment           code
    427 --------------------------------------------------------------------------------
    428 Python                          703          20009          21411         102180
    429 Bourne Shell                     13            643            678           3911
    430 Bourne Again Shell               44            583            434           3172
    431 diff                             17             31            298            574
    432 YAML                              6             32             80            573
    433 XSD                               1             27             27            494
    434 C                                 2             56            128            291
    435 make                              1              7              6             19
    436 INI                               1              1              0             15
    437 reStructuredText                  1              5              4              9
    438 XSLT                              1              0              0              5
    439 --------------------------------------------------------------------------------
    440 SUM:                            790          21394          23066         111243
    441 --------------------------------------------------------------------------------
    442 </code></pre>
    444 <p>That&rsquo;s quite a lot.</p>
    446 <p>Portage is a package manager that tries to ease the configuration process of
    447 packages, but at the process makes it terribly complex to compose packages,
    448 and adds billions of portage configuration options. Configuring your first
    449 kernel is literally easier than configuring portage in a way you want. Users
    450 just do not know that they would be better off doing an LFS build for a much
    451 stabler system. My system was broken countless times while using Gentoo.
    452 Maintaining a Gentoo system is honestly harder than maintaining my own
    453 distribution.</p>
    455 <p><strong>EAPI</strong>, probably the worst thing about the Portage ecosystem. It is the most
    456 complex, hard to read, hard to learn packaging system ever made. Creating a
    457 USE flag system shouldn&rsquo;t have been this hard.</p>
    459 <hr />
    461 <h1>Editor Wizardry</h1>
    463 <p><a href="/blog/20200413-editor-wizardry.html">Permalink</a></p>
    465 <p>Date: Apr 13 2020</p>
    467 <p>To this day, I have tried lots of IDEs and text editors. Visual Studio, PyCharm,
    468 Sublime, Notepad++, Vim, Emacs, Pico, Atom, etc. The list goes on. I have even
    469 unironically used ed, and ironically used cat for a while.</p>
    471 <p>I have settled down after years and years of &ldquo;editor-hopping&rdquo;. I now have 3
    472 main editors that I use on a daily basis! Yeah, you have read it correct. I use
    473 3 editors on a daily basis. Those are,</p>
    475 <ul>
    476 <li>sed</li>
    477 <li>vi (not vim)</li>
    478 <li>emacs</li>
    479 </ul>
    482 <h2>Emacs</h2>
    484 <p>Emacs is a beast. Defining Emacs as a text-editor is wrong. It is a lisp
    485 interpreter, with text manipulation abilities.</p>
    487 <p>Now, I do like the concept of Integrated Development Environments. It&rsquo;s a shame
    488 that all of them suck. With Emacs I can fine-tune everything according to my
    489 taste, install the packages I need, configure them the way I like. With IDEs you
    490 get some nice plugins, and a tiny bit of customization, but that&rsquo;s it. You get
    491 an environment limited by the vision of someone else. Not to mention that most
    492 IDEs are proprietary software.</p>
    494 <p>I have stopped using Vim, because it is only meant to be a text editor. You can
    495 extend its features with plugins, but you can really see the impact with just a
    496 few of them. Vimscript is also really primitive, that&rsquo;s why people write plugins
    497 with Python, JS, and such. This further affects the speed of Vim. Most Emacs
    498 packages I have encountered are written in pure lisp. I have over 70 packages,
    499 yet my load time and overall speed is better than when I had Vim with 8 plugins.</p>
    501 <h3>Cons</h3>
    503 <ul>
    504 <li><strong>It comes with too many unnecessary features</strong> Emacs comes with multiple IRC
    505 clients, a mail reader, rss reader etc. I don&rsquo;t think they are badly
    506 implemented or anything, I would just prefer building up as I want to instead.</li>
    507 <li><strong>The defaults are not intuitive</strong> Now, I haven&rsquo;t actually tried any of them,
    508 but there is a reason &ldquo;Emacs distributions&rdquo;, such as &ldquo;Spacemacs&rdquo;, &ldquo;DOOM
    509 Emacs&rdquo;, &ldquo;Centaur&rdquo; exist. The base of Emacs, even with its unnecessary
    510 features, is unintuitive and complicated.</li>
    511 </ul>
    514 <p>Also, let&rsquo;s not forget that Emacs uses an ancient Lisp dialect.</p>
    516 <h2>Vi</h2>
    518 <p>I mostly use Emacs when I am dealing with projects. If my aim is to just make
    519 simple changes when I am on the terminal, I just pop up vi provided by busybox.
    520 I just like that it is fast and featureless. It barely gets the job done, and
    521 that&rsquo;s why I like it.</p>
    523 <h3>Cons</h3>
    525 <ul>
    526 <li><strong>No syntax highlighting</strong> Syntax highlighting is an important feature for me
    527 but I have learned to live without it. Since I don&rsquo;t edit large files with it,
    528 this is not the biggest con.</li>
    529 <li><strong>Hard to configure</strong> Busybox vi only has a limited featureset, which makes
    530 it hard to configure. It doesn&rsquo;t read an <code>rc</code> file, it uses the <code>$EXINIT</code>
    531 variable instead. Available options are limited. For example, you cannot
    532 convert the &ldquo;tab&rdquo; action to use space instead of tabs.</li>
    533 <li><strong>No visual selection support</strong> Sadly, <code>v/V</code> isn&rsquo;t implemented in busybox vi.</li>
    534 </ul>
    537 <h2>Sed</h2>
    539 <p>I use sed for when I am making small changes to small files, because it is
    540 faster than opening a file, making a change, saving, and exiting. Using regular
    541 expressions are much faster and efficient at such things.</p>
    543 <h3>Cons</h3>
    545 <ul>
    546 <li><strong>Risky unless you know what you are doing</strong> Since sed is operated on regex,
    547 you need to be extra careful. You are running that regex on the entire file
    548 without an option to &lsquo;undo&rsquo; (unless you have a sed that has a backup
    549 implementation).</li>
    550 </ul>
    552     </p>
    553     <a href="/index.txt">This page in plain-text</a>
    554     <hr>
    555     <p class=footer>Copyright &copy; 2019-2021 Cem Keylan</p>
    556   </body>
    557 </html>