Show HN: Gemini web client in 100 lines of C (github.com)
Gemini protocol documentation claims that it is possible to write basic web client in 100 lines of code proving protocol simplicity. Easy in modern scripting language but can it be done in ANSI C? Let the source code decide.
Someone suggested to share this silly project of mine with HN community so here it is. Enjoy
46 comments
[ 6.0 ms ] story [ 132 ms ] thread"Gemini" is defined as servers and client communicating using the "Gemini" protocol (and content is usually in the gemtext format).
Both are part of the Internet, which is defined as an INTERconnection of NETworks, thus a physical worldwide network of computers. Any computer with a public IP (Internet Address) is thus part of the Internet (in your house, is usually your box which is part of the Internet and acting as a gateway for your computer).
So : Internet is mostly an hardware network. You then join different part of the Internet by running adhoc software: Web, Gemini, Gopher, Mail, FTP, Usenet, etc…
Those have very clear and crisp definitions, there absolutely nothing to argue about. A "Web Gemini client" means a browser capable of accessing both Gemini and the Web, which is not the case here. It is a "Gemini browser".
It could also be a Gemini client running on the web, which is what I initially thought it would be from reading the title.
It's basically the Web but instead of HTTP it uses its own protocol over TCP thus still within the Internet protocol suite. Qualifying it as the "Gemini web" already communicates that just fine and is less less whimsy than Geminiverse or whatever.
I suppose I could see grounds for suggesting OP use the community-preferred vernacular as a personal preference, but that's about all the bite I see in this bark. Maybe that's reason enough to change it so you don't piss off Gemini's 12 users—I can grant that one.
Also, "Gemini web client" does make it sounds like it's a web client for Gemini. I'd just change it to "a client for the Gemini web".
In spoken human language we have this concept called connotation, "web" being associated with http servers and clients is one example of this.
There is no rule that a word must only have one connotation, and Gemni is very much a "a complex system of interconnected elements".
So basically, this is just your opinion and you aren't the authority on what the word "web" means, and it's okay to tell people that the word is often used in a context they might not have known about, but claiming that "there is nothing to argue" is not correct.
These line-based browsers used to be more common, there were a few ones for the www but also ‘ftp’ has such a mode. As does my little ‘nostt’ Teletext reader.
Gemini, not Gopher.
"Gemini web" is a bad name because Gemini's not being part of the web was the main motive in Gemini's creation.
"Geminisphere" or "Geminiverse" would be fine with me as a name for the totality of Gemini servers considered collectively.
It even supports mime types, you can serve text/gemini from an HTTP server, or a text/html file from a Gemini server.
It would have been a great opportunity to use BearSSL[0].
0. https://bearssl.org/
0. https://bearssl.org/gitweb/?p=BearSSL;a=summary
For example:
Could become:Thanks for suggestion. I will go through code again to see if I can save more space with normal code.
Actually that was my workflow. Each time I managed to write something in simpler way I reverted few tricks.
Also
is a potential buffer overflow where `long` is 64 bits since only the first four bytes of `h_addr_list[i]` can be accessed, and also potentially misaligned for `unsigned long`; and it will also not work correctly on big endian platforms where `long` is 64 bits. Using `memcpy()` would have avoided all these problems. I am really confused as to how you arrived at the conclusion that "yes, this is the way to copy 4 bytes from A to B".This sounds rude, I know, and I apologize; I don't want to single you/this project out personally. I am just frustrated that even today there are people who work on C/C++ projects seemingly without having made GCC's -fanalyzer/asan/ubsan/valgrind/etc. an important part of their development workflow.
Not to make any excuses, just for the context. I'm a beginner in C programming and I wrote only few small programs. My tooling is basically non existent. I will try to improve tho.
I'd look for it, as a C programmer. On phone now, so sadly don't have it handy.
¹ https://nullprogram.com/blog/2023/04/29/
² https://news.ycombinator.com/item?id=35758898
1. `sfd` is now closed. This cost me an extra line of code tho so I had to improvise.
2. `j` was not initially -1 as even empty input contains one "\n" character. But it could go pass value of 0 underflowing the buffer when buffer contained only white space characters (like in case of single "\n"). This was corrected.
3. `h_addr_list[i]` about this one. I'm quite sure I took it from example in one of man pages. Anyway, I replaced it with `memcpy`.
I also tried to use valgrind. This is OFC my first step but I will continue my studies on detecting memory leaks in future projects.
[1] https://code.jsoftware.com/wiki/Essays/Incunabulum
The most unreadable code would be the code that was intentionally obfuscated. Here I at least try to pretend that it is readable in some parts.
Thanks for kind words.
> Yeah. I started a project a couple months ago. I’m slowly porting that code base, the J source code to C++ 20. And yeah, I think there should be a name for these types of code bases, because it’s not C, it’s like a macro variant of C, where it’s a CDSL, where 80% of your “library” is macros. I did a search, there’s 10,000 macros in the source code, and those macros are used like functions.
Idea is that using goto your program can be mostly a loop where you make forward jumps to anywhere in the code & everything converges or jumps back to the start. It ends up making for very terse code since you're no longer passing values back & forth as much through structs/params/returns. You align variables & jump to code. Each label has a kind of ABI where the variables are the registers
On golang, Bombadillo today supports "images" with Unicode-art and searching inside the "pages" a la ctrl-f in browsers, so it's better than Amfora and one of the best TUI clients ever. Also, it does gopher and https thru external tools like lynx.
:)
This is a minimal Gemini protocol client written in C. The Gemini protocol is an application-level internet protocol for serving hypertext documents over secure connections.
The code includes various standard C libraries and the OpenSSL library for secure connections. The `WARN` macro definition is used to print warning messages to `stderr` and jump to the `start:` label.
In the `main` function, it defines variables and initializes certain structs, like `struct sockaddr_in addr` that is used for internet socket addresses. It then opens a connection with the Gemini server on port 1965 (the standard port for the Gemini protocol). The connection is encrypted using the OpenSSL library functions. It also opens two file streams, one for history (`.gmi100`) and another for a temporary file. The file names and locations are generated by the `tmpnam()` function.
The main loop reads commands from `stdin`. Depending on the command, it can execute system commands (when the input starts with '!'), navigate through the history (when input starts with a digit), process a variety of single-letter commands (like 'q' for quit, 'r' for refresh, 'c' to print current URI, 'u' for up a level, 'b' for back in history), or interpret the input as a URL or a part of a URL to be navigated to.
For URL navigation, it creates a TCP socket connection with the Gemini server, sets up an SSL connection over that socket, and sends a request to the server for the resource identified by the URL. The client handles the server response depending on the status code. For example, if the status code is '1' it's a request for input and if it's '3' it's a redirect. It prints the response to `stdout` and also stores it in the temporary file.
For `text/*` type responses, it additionally parses any lines that start with '=>' as links and assigns them a number, storing this in the temporary file for later navigation. If the line starts with '=>' but doesn't have a description, it goes back to reading the rest of the description.
After processing the server response, it uses the `less` command to display the content of the temporary file, allowing the user to scroll through it if it's longer than the screen. Before each new iteration of the main loop, it flushes the temporary file and goes back to the `start:` label. If the user quits, the function returns 0 and the program terminates.
Overall, this program is a command-line based Gemini client that allows a user to navigate the geminispace, issue queries, and view text documents served over the Gemini protocol. It also keeps a history of visited URIs in the `.gmi100` file.
source: https://sharegpt.com/c/xscNWg6
> navigate through the history (when input starts with a digit)
Should be more like: navigate to link from currently displayed page (when input starts with a digit).
> It prints the response to `stdout` and also stores it in the temporary file.
Response is not printed to `stdout` at all. It is only stored in temporary file.
Also the possibility to use first program argument to overwrite the `less` command was ignored.
But overall very good job.