Whole bunch of changes and releases

This commit is contained in:
Yorhel 2018-01-29 14:32:46 +01:00
parent 07143cfb36
commit 54947f15c2
36 changed files with 3269 additions and 40 deletions

View file

@ -8,6 +8,10 @@ rare occasions are published on this page.
=over
=item C<2017-05-28 > - L<An Opinionated Survey of Functional Web Development|https://dev.yorhel.nl/doc/funcweb>
The title says it all.
=item C<2014-07-29 > - L<The Sorry State of Convenient IPC|https://dev.yorhel.nl/doc/easyipc>
A long rant about IPC systems.

571
dat/doc-funcweb Normal file
View file

@ -0,0 +1,571 @@
An Opinionated Survey of Functional Web Development
=pod
(Published on B<2017-05-28>.)
=head1 Intro
TL;DR: In this article I provide an overview of the frameworks and libraries
available for creating websites in statically-typed functional programming
languages.
I recommend you now skip directly to the next section, but if you're interested
in some context and don't mind a rant, feel free to read on. :-)
B<< <Rant mode> >>
When compared to native desktop application development, web development just
sucks. Native development is relatively simple with toolkits such as
L<Qt|https://www.qt.io/>, L<GTK+|https://www.gtk.org/> and others: You have
convenient widget libraries, and you can describe your entire application, from
interface design to all behavioural aspects, in a single programming language.
You're also largely free to structure code in whichever way makes most sense.
You can describe what a certain input field looks like, what happens when the
user interacts with it and what will happen with the input data, all succinctly
in a single file. There are even drag-and-drop UI builders to speed up
development.
Web development is the exact opposite of that. There are several different
technologies you're forced to work with even when creating the most mundane
website, and there's a necessary but annoying split between code that runs on
the server and code that runs in the browser. Creating a simple input field
requires you to consider and maintain several ends:
=over
=item
The back end (server-side code) that describes how the input field interacts
with the database.
=item
Some JavaScript code to describe how the user can interact with the input
field.
=item
Some CSS to describe what the input field looks like.
=item
And then there's HTML to act as a glue between the above.
=back
In many web development setups, all four of the above technologies are
maintained in different files. If you want to add, remove or modify an input
field, or just about anything else on a page, you'll be editing at least four
different files with different syntax and meaning. I don't know how other
developers deal with this, but the only way I've been able to keep these places
synchronized is to just edit one or two places, test if it works in a browser,
and then edit the other places accordingly to fix whatever issues I find. This
doesn't always work well: I don't get a warning if I remove an HTML element
somewhere and forget to also remove the associated CSS. Heck, in larger
projects I can't even tell whether it's safe to remove or edit a certain line
of CSS because I have no way to know for sure that it's not still being used
elsewhere. Perhaps this particular case can be solved with proper organization
and discipline, but similar problems exist with the other technologies.
Yet despite that, why do I still create websites in my free time? Because it is
the only environment with high portability and low friction - after all, pretty
much anyone can browse the web. I would not have been able to create a useful
"L<Visual Novel Database|https://vndb.org/>" any other way than through a
website. And the entire purpose of L<Manned.org|https://manned.org/> is to
provide quick access to man pages from anywhere, which is not easily possible
with native applications.
B<< </Rant mode> >>
Fortunately, I am not the only one who sees the problems with the "classic"
development strategy mentioned above. There are many existing attempts to
improve on that situation. A popular approach to simplify development is the
L<Single-page
application|https://en.wikipedia.org/wiki/Single-page_application> (SPA). The
idea is to move as much code as possible to the front end, and keep only a
minimal back end. Both the HTML and the entire behaviour of the page can be
defined in the same language and same file. With libraries such as
L<React|https://facebook.github.io/react/> and browser support for L<Web
components|https://developer.mozilla.org/en-US/docs/Web/Web_Components>, the
split between files described above can be largely eliminated. And if
JavaScript isn't your favorite language, there are many alternative languages
that compile to JavaScript. (See L<The JavaScript
Minefield|http://walkercoderanger.com/blog/2014/02/javascript-minefield/> for
an excellent series of articles on that topic).
While that approach certainly has the potential to make web development more
pleasant, it has a very significant drawback: Performance. For some
applications, such as web based email clients or CRM systems, it can be
perfectly acceptable to have a megabyte of JavaScript as part of the initial
page load. But for most other sites, such as this one, or the two sites I
mentioned earlier, or sites like Wikipedia, a slow initial page load is
something I consider to be absolutely unacceptable. The web can be really fast,
and developer laziness is not a valid excuse to ruin it. (If you haven't seen
or read L<The Website Obesity
Crisis|http://idlewords.com/talks/website_obesity.htm> yet, please do so now).
I'm much more interested in the opposite approach to SPA: Move as much code as
possible to the back end, and only send a minimal amount of JavaScript to the
browser. This is arguably how web development has always been done in the past,
and there's little reason to deviate from it. The difference, however, is that
people tend to expect much more "interactivity" from web sites nowadays, so the
amount of JavaScript is increasing. And that is alright, so long as the
JavaScript doesn't prevent the initial page from loading quickly. But this
increase in JavaScript does amplify the "multiple files" problem I ranted about
earlier.
So my ideal solution is a framework where I can describe all aspects of a site
in a single language, and organize the code among files in a way that makes
sense to me. That is, I want the same kind of freedom that I get with native
desktop software development. Such a framework should run on the back end, and
automatically generate efficient JavaScript and, optionally, CSS for the front
end. As an additional requirement (or rather, strong preference), all this
should be in a statically-typed language - because I am seemingly incapable of
writing large reliable applications with dynamic typing - and in a language
from functional heritage - because programming in functional languages has
spoiled me.
I'm confident that what I describe is possible, and it's evident that I'm not
the only person to want this, as several (potential) solutions like this do
indeed exist. I've been looking around for these solutions and have
experimented with a few that looked promising. This article provides an
overview of what I have found so far.
=head1 OCaml
My adventure began with L<OCaml|https://ocaml.org/>. It's been a few years
since I last used OCaml for anything, but development on the language and its
ecosystem have all but halted. L<Real World OCaml|https://realworldocaml.org/>
has been a great resource to get me up to speed again.
=head2 Ocsigen
For OCaml there is one project that has it all: L<Ocsigen|http://ocsigen.org/>.
It comes with an OCaml to JavaScript compiler, a web server, several handy
libraries, and a L<framework|http://ocsigen.org/eliom/> to put everything
together. Its L<syntax
extension|http://ocsigen.org/eliom/6.2/manual/ppx-syntax> allows you to mix
front and back end code, and you can easily share code between both ends. The
final result is a binary that runs the server and a JavaScript file that
handles everything on the client side.
The framework comes with an embedded DSL with which you can conveniently
generate HTML without actually typing HTML. And best of all, this DSL works on
both the client and the server: On the server side it generates an HTML string
that can be sent to the client, and running the same code on the client side
will result in a DOM element that is ready to be used.
Ocsigen makes heavy use of the OCaml type system to statically guarantee the
correctness of various aspects of the application. The HTML DSL ensures not
only that the generated HTML well-formed, but also prevents you from
incorrectly nesting certain elements and using the wrong attributes on the
wrong elements. Similarly, an HTML element generated on the server side can be
referenced from client side code without having to manually assign a unique ID
to the element. This prevents accidental typos in the ID naming and guarantees
that the element that the client side code refers to actually exists. URL
routing and links to internal pages are also checked at compile time.
Ocsigen almost exactly matches what I previously described as the perfect
development framework. Unfortunately, it has a few drawbacks:
=over
=item
The generated JavaScript is quite large, a bit over 400 KiB for an hello world.
In my brief experience with the framework, this also results in a noticeably
slower page load. I don't know if it was done for performance purposes, but
subsequent page views are per default performed via in-browser XHR requests,
which do not require that all the JavaScript is re-parsed and evaluated, and is
thus much faster. This, however, doesn't work well if the user opens pages in
multiple tabs or performs a page reload for whatever reason. And as I
mentioned, I care a lot about the initial page loading time.
=item
The framework has a steep learning curve, and the available documentation is by
far not complete enough to help you. I've found myself wondering many times how
I was supposed to use a certain API and have had to look for example code for
enlightenment. At some point I ended up just reading the source code instead of
going for the documentation. What doesn't help here is that, because of the
heavy use of the type system to ensure code correctness, most of the function
signatures are far from intuitive and are sometimes very hard to interpret.
This problem is made even worse with the generally unhelpful error messages
from the compiler. (A few months with L<Rust|https://www.rust-lang.org/> and
its excellent error messages has really spoiled me on this aspect, I suppose).
=item
I believe they went a bit too far with the compile-time verification of certain
correctness properties. Apart from making the framework harder to learn, it
also increases the verbosity of the code and removes a lot of flexibility. For
instance, in order for internal links to be checked, you have to declare your
URLs (or I<services>, as they call it) somewhere central such that the view
part of your application can access it. Then elsewhere you have to register a
handler to that service. This adds boilerplate and enforces a certain code
structure. And the gain of all this is, in my opinion, pretty small: In the 15
years that I have been building web sites, I don't remember a single occurrence
where I mistyped the URL in an internal link. I do suppose that this feature
makes it easy to change URLs without causing breakage, but there is a trivial
counter-argument to that: L<Cool URIs don't
change|https://www.w3.org/Provider/Style/URI.html>. (Also, somewhat ironically,
I have found more dead internal links on the Ocsigen website than on any other
site I have visited in the past year, so perhaps this was indeed a problem they
considered worth fixing. Too bad it didn't seem to work out so well for them).
=back
Despite these drawbacks, I am really impressed with what the Ocsigen project
has achieved, and it has set a high bar for the future frameworks that I will
be considering.
=head1 Haskell
I have always seen Haskell as that potentially awesome language that I just
can't seem to wrap my head around, despite several attempts in the past to
learn it. Apparently the only thing I was missing in those attempts was a
proper goal: When I finally started playing around with some web frameworks I
actually managed to get productive in Haskell with relative ease. What also
helped me this time was a practical introductory Haskell reference, L<What I
Wish I Knew When Learning Haskell|http://dev.stephendiehl.com/hask/>, in
addition to the more theoretical L<Learn You A Haskell for Great
Good|http://learnyouahaskell.com/>.
Haskell itself already has a few advantages when compared to OCaml: For one, it
has a larger ecosystem, so for any task you can think of there is probably
already at least one existing library. As an example, I was unable to find an
actively maintained SQL DSL for OCaml, while there are several available for
Haskell. Another advantage that I found where the much more friendly and
detailed error messages generated by the Haskell compiler, GHC. In terms of
build systems, Haskell has standardized on
L<Cabal|https://www.haskell.org/cabal/>, which works alright most of the time.
Packaging is still often complex and messy, but it's certainly improving as
L<Stack|http://haskellstack.org/> is gaining more widespread adoption. Finally,
I feel that the Haskell syntax is slightly less verbose, and more easily lends
itself to convenient DSLs.
Despite Haskell's larger web development community, I could not find a single
complete and integrated client/server development framework such as Ocsigen.
Instead, there are a whole bunch of different projects focussing on either the
back end or the front end. I'll explore some of them with the idea that,
perhaps, it's possible to mix and match different libraries and frameworks in
order to get the perfect development environment. And indeed, this seems to be
a common approach in many Haskell projects.
=head2 Server-side
Let's start with a few back end frameworks.
=over
=item Scotty
L<Scotty|https://github.com/scotty-web/scotty> is a web framework inspired by
L<Sinatra|http://www.sinatrarb.com/>. I have no experience with (web)
development in Ruby and have never used Sinatra, but it has some similarities
to what I have been using for a long time: L<TUWF|https://dev.yorhel.nl/tuwf>.
Scotty is a very minimalist framework; It does routing (that is, mapping URLs
to Haskell functions), it has some functions to access request data and some
functions to create and modify a response. That's it. No database handling,
session management, HTML generation, form handling or other niceties. But
that's alright, because there are many generic libraries to help you out there.
Thanks to its minimalism, I found Scotty to be very easy to learn and get used
to. Even as a Haskell newbie I had a simple website running within a day. The
documentation is appropriate, but the idiomatic way of combining Scotty with
other libraries is through the use of Monad Transformers, and a few more
examples in this area would certainly have helped.
=item Spock
Continuing with the Star Trek franchise, there's
L<Spock|https://www.spock.li/>. Spock is very similar to Scotty, but comes with
type-safe routing and various other goodies such as session and state
management, L<CSRF|https://en.wikipedia.org/wiki/Cross-site_request_forgery>
protection and database helpers.
As with everything that is (supposedly) more convenient, it also comes with a
slightly steeper learning curve. I haven't, for example, figured out yet how to
do regular expression based routing. I don't even know if that's still possible
in the latest version - the documentation isn't very clear. Likewise, it's
unclear to me what the session handling does exactly (Does it store something?
And where? Is there a timeout?) and how that interacts with CSRF protection.
Spock seems useful, but requires more than just a cursory glance.
=item Servant
L<Servant|http://haskell-servant.github.io/> is another minimalist web
framework, although it is primarily designed for creating RESTful APIs.
Servant distinguishes itself from Scotty and Spock by not only featuring
type-safe routing, it furthermore allows you to describe your complete public
API as a type, and get strongly typed responses for free. This also enables
support for automatically generated documentation and client-side API wrappers.
Servant would be an excellent back end for a SPA, but it does not seem like an
obvious approach to building regular websites.
=item Happstack / Snap / Yesod
L<Happstack|http://www.happstack.com/>, L<Yesod|http://www.yesodweb.com/> and
L<Snap|http://snapframework.com/> are three large frameworks with many
auxiliary libraries. They all come with a core web server, routing, state and
database management. Many of the libraries are not specific to the framework
and can be used together with other frameworks. I won't go into a detailed
comparison between the three projects because I have no personal experience
with any of them, and fortunately L<someone else already wrote a
comparison|http://softwaresimply.blogspot.nl/2012/04/hopefully-fair-and-useful-comparison-of.html>
in 2012 - though I don't know how accurate that still is today.
=back
So there are a fair amount of frameworks to choose from, and they can all work
together with other libraries to implement additional functions. Apart from the
framework, another important aspect of web development is how you generate the
HTML to send to the client. In true Haskell style, there are several answers.
For those who prefer embedded DSLs, there are
L<xhtml|http://hackage.haskell.org/package/xhtml>,
L<BlazeHTML|https://jaspervdj.be/blaze/> and
L<Lucid|https://github.com/chrisdone/lucid>. The xhtml package is not being
used much nowadays and has been superseded by BlazeHTML, which is both faster
and offers a more readable DSL using Haskell's do-notation. Lucid is heavily
inspired by Blaze, and attempts to L<fix several of its
shortcomings|http://chrisdone.com/posts/lucid>. Having used Lucid a bit myself,
I can attest that it is easy to get started with and pretty convenient in use.
I definitely prefer to generate HTML using DSLs as that keeps the entire
application in a single host language and with consistent syntax, but the
alternative approach, templating, is also fully supported in Haskell. The Snap
framework comes with L<Heist|https://github.com/snapframework/heist>, which are
run-time interpreted templates, like similar systems in most other languages.
Yesod comes with L<Shakespeare|http://hackage.haskell.org/package/shakespeare>,
which is a type-safe templating system with support for inlining the templates
in Haskell code. Interestingly, Shakespeare also has explicit support for
templating JavaScript code. Too bad that this doesn't take away the need to
write the JavaScript yourself, so I don't see how this is an improvement over
some other JavaScript solution that uses JSON for communication with the back
end.
=head2 Client-side
It is rather unusual to have multiple compiler implementations targeting
JavaScript for the same source language, but Haskell has three of them. All
three can be used to write front end code without touching a single line of
JavaScript, but there are large philosophical differences between the three
projects.
=over
=item Fay
L<Fay|https://github.com/faylang/fay/wiki> compiles Haskell code directly to
JavaScript. The main advantage of Fay is that it does not come with a large
runtime, resulting small and efficient JavaScript. The main downside is that it
only L<supports a subset of
Haskell|https://github.com/faylang/fay/wiki/What-is-not-supported?>. The
result is a development environment that is very browser-friendly, but where
you can't share much code between the front and back ends. You're basically
back to the separated front and back end situation in classic web development,
but at least you can use the same language for both - somewhat.
Fay itself doesn't come with many convenient UI libraries, but
L<Cinder|http://crooney.github.io/cinder/index.html> covers that with a
convenient HTML DSL and DOM manipulation library.
Fay is still seeing sporadic development activity, but there is not much of a
lively community around it. Most people have moved on to other solutions.
=item GHCJS
L<GHCJS|https://github.com/ghcjs/ghcjs> uses GHC itself to compile Haskell to a
low-level intermediate language, and then compiles that language to JavaScript.
This allows GHCJS to achieve excellent compatibility with native Haskell code,
but comes, quite predictably, at the high cost of duplicating a large part of
the Haskell runtime into the JavaScript output. The generated JavaScript code
is typically measured in megabytes rather than kilobytes, which is (in my
opinion) far too large for regular web sites. The upside of this high
compatibility, of course, is that you can re-use a lot of code between the
front and back ends, which will certainly make web development more tolerable.
The community around GHCJS seems to be more active than that of Fay. GHCJS
integrates properly with the Stack package manager, and there are a L<whole
bunch|http://hackage.haskell.org/packages/search?terms=ghcjs> of libraries
available.
=item Haste
L<Haste|https://github.com/valderman/haste-compiler> provides a middle ground
between Fay and GHCJS. Like GHCJS, Haste is based on GHC, but it instead of
using low-level compiler output, Haste uses a higher-level intermediate
language. This results in good compatibility with regular Haskell code while
keeping the output size in check. Haste has a JavaScript runtime of around 60
KiB and the compiled code is roughly as space-efficient as Fay.
While it should be possible to share a fair amount of code between the front
and back ends, not all libraries work well with Haste. I tried to use Lucid
within a Haste application, for example, but that did not work. Apparently one
of its dependencies (probably the UTF-8 codec, as far as I could debug the
problem) performs some low-level performance optimizations that are
incompatible with Haste.
Haste itself is still being sporadically developed, but not active enough to be
called alive. The compiler lags behind on the GHC version, and the upcoming 0.6
version has stayed unreleased and in limbo state for at least 4 months on the
git repository. The community around Haste is in a similar state. Various
libraries do exist, such as L<Shade|https://github.com/takeoutweight/shade>
(HTML DSL, Reactive UI), L<Perch|https://github.com/agocorona/haste-perch>
(another HTML DSL), L<haste-markup|https://github.com/ajnsit/haste-markup> (yet
another HTML DSL) and
L<haste-dome|https://github.com/wilfriedvanasten/haste-dome> (I<yet> another
HTML DSL), but they're all pretty much dead.
=back
Despite having three options available, only Haste provides enough benefit of
code reuse while remaining efficient enough for the kind of site that I
envision. Haste really deserves more love than it is currently getting.
=head2 More Haskell
In my quest for Haskell web development frameworks and tools, I came across a
few other interesting libraries. One of them is
L<Clay|http://fvisser.nl/clay/>, a CSS preprocessor as a DSL. This will by
itself not solve the CSS synchronisation problem that I mentioned at the start
of this article, but it could still be used to keep the CSS closer to code
implementing the rest of the site.
It also would not do to write an article on Haskell web development and not
mention a set of related projects: L<MFlow|https://github.com/agocorona/MFlow>,
L<HPlayground|https://github.com/agocorona/hplayground> and the more recent
L<Axiom|https://github.com/transient-haskell/axiom>. These are ambitious
efforts at building a very high-level and functional framework for both front
and back end web development. I haven't spend nearly enough time on these
projects to fully understand their scope, but I'm afraid of these being a bit
too high level. This invariably results in reduced flexibility (i.e. too many
opinions being hard-coded in the API) and less efficient JavaScript output.
Axiom being based on GHCJS reinforces the latter concern.
=head1 Other languages
I've covered OCaml and Haskell now, but there are relevant projects in other
languages, too:
=over
=item PureScript
L<PureScript|http://www.purescript.org/> is the spiritual successor of Fay -
except it does not try to be compatible with Haskell, and in fact
L<intentionally deviates from
Haskell|https://github.com/purescript/documentation/blob/master/language/Differences-from-Haskell.md>
at several points. Like Fay, and perhaps even more so, PureScript compiles down
to efficient and small JavaScript.
Being a not-quite-Haskell language, sharing code between a PureScript front end
and a Haskell back end is not possible, the differences are simply too large.
It is, however, possible to go into the other direction: PureScript could also
run on the back end in a NodeJS environment. I don't really know how well this
is supported by the language ecosystem, but I'm not sure I'm comfortable with
replacing the excellent quality of Haskell back end frameworks with a fragile
NodeJS back end (or such is my perception, I admittedly don't have too much
faith in most JavaScript-heavy projects).
The PureScript community is very active and many libraries are available in the
L<Persuit|https://pursuit.purescript.org/> package repository. Of note is
L<Halogen|https://pursuit.purescript.org/packages/purescript-halogen>, a
high-level reactive UI library. One thing to be aware of is that not all
libraries are written with space efficiency as their highest priority, the
simple L<Halogen
button|https://github.com/slamdata/purescript-halogen/tree/v2.0.1/examples/basic>
example already compiles down to a hefty 300 KB for me.
=item Elm
L<Elm|http://elm-lang.org/> is similar to PureScript, but rather than trying to
be a generic something-to-JavaScript compiler, Elm focuses exclusively on
providing a good environment to create web UIs. The reactive UI libraries are
well maintained and part of the core Elm project. Elm has a strong focus on
being easy to learn and comes with good documentation and many examples to get
started with.
=item Ur/Web
L<Ur/Web|http://www.impredicative.com/ur/> is an ML and Haskell inspired
programming language specifically designed for client/server programming. Based
on its description, Ur/Web is exactly the kind of thing I'm looking for: It
uses a single language for the front and back ends and provides convenient
methods for communication between the two.
This has been a low priority on my to-try list because it seems to be primarily
a one-man effort, and the ecosystem around it is pretty small. Using Ur/Web for
practical applications will likely involve writing your own libraries or
wrappers for many common tasks, such as for image manipulation or advanced text
processing. Nonetheless, I definitely should be giving this a try sometime.
(Besides, who still uses frames in this day and age? :-)
=item Opa
I'll be moving out of the functional programming world for a bit.
L<Opa|http://opalang.org/> is another language and environment designed for
client/server programming. Opa takes a similar approach to "everything in
PureScript": Just compile everything to JavaScript and run the server-side code
on NodeJS. The main difference with other to-JavaScript compilers is that Opa
supports mixing back end code with front end code, and it can automatically
figure out where the code should be run and how the back and front ends
communicate with each other.
Opa, as a language, is reminiscent of a statically-typed JavaScript with
various syntax extensions. While it does support SQL databases, its database
API seems to strongly favor object-oriented use rather than relational database
access.
=item GWT
Previously I compared web development to native GUI application development.
There is no reason why you can't directly apply native development structure
and strategies onto the web, and that's exactly what
L<GWT|http://www.gwtproject.org/> does. It provides a widget-based programming
environment that eventually runs on the server and compiles the client-side
part to JavaScript. I haven't really considered it further, as Java is not a
language I can be very productive in.
=item Webtoolkit
In the same vein, there's L<Wt|https://www.webtoolkit.eu/wt>. The name might
suggest that it is a web-based clone of Qt, and indeed that's what it looks
like. Wt is written in C++, but there are wrappers for L<other
languages|https://www.webtoolkit.eu/wt/other_language>. None of the languages
really interest me much, however.
That said, if I had to write a web UI for a resource-constrained device, this
seems like an excellent project to consider.
=back
=head1 To conclude
To be honest, I am a bit overwhelmed at the number of options. On the one hand,
it makes me very happy to see that a lot is happening in this world, and that
alternatives to boring web frameworks do exist. Yet after all this research I
still have no clue what I should use to develop my next website. I do like the
mix and match culture of Haskell, which has the potential to form a development
environment entirely to my own taste and with my own chosen trade-offs. On the
other hand, the client-side Haskell solutions are simply too immature and
integration with the back end frameworks is almost nonexistent.
Almost none of the frameworks I discussed attempt to tackle the CSS problem
that I mentioned in the introduction, so there is clearly room for more
research in this area.
There are a few technologies that I should spend more time on to familiarize
myself with. Ur/Web is an obvious candidate here, but perhaps it is possible to
create a Haskell interface to Wt. Or maybe some enhancements to the Haste
ecosystem could be enough to make that a workable solution instead.

View file

@ -27,7 +27,7 @@ Download: L<0.3|http://p.blicky.net/h25z8>
September 2011. L<ncdc|https://dev.yorhel.nl/ncdc> gained transfer logging
features, and I wrote a quick Perl script to fetch some simple statistics from
it. L<source|http://p.blicky.net/eu00a> (L<0.1|http://p.blicky.net/agolr>).
it. L<source|https://p.blicky.net/4V9Kg59kUJUN> (L<0.2|http://p.blicky.net/eu00a>, L<0.1|http://p.blicky.net/agolr>).
=head2 json.mll

View file

@ -49,10 +49,11 @@ L<Frugalware|http://frugalware.org/packages?srch=ncdc&op=pkg&arch=all&ver=all> -
L<Gentoo|http://packages.gentoo.org/package/net-p2p/ncdc> -
L<GNU Guix|https://www.gnu.org/software/guix/package-list.html> -
L<Homebrew|http://braumeister.org/formula/ncdc> -
L<OpenSUSE|http://packman.links2linux.org/package/ncdc>
L<OpenSUSE|http://packman.links2linux.org/package/ncdc> -
L<Source Mage|http://download.sourcemage.org/grimoire/codex/test/ftp/ncdc/>
I have a few old packages on the L<Open Build
Service|https://build.opensuse.org/package/show?package=ncdc&project=home%3Ayorhel>,
Service|https://build.opensuse.org/package/show/home:yorhel/ncdc>,
but these are unmaintained. The static binaries are preferred.
A convenient installer is available for
@ -60,10 +61,6 @@ L<Android|http://code.ivysaur.me/ncdcinstaller.html>.
=back
=cut
[html]<iframe src="http://software.opensuse.org/download/package.iframe?project=home:yorhel&package=ncdc" style="width: 560px; height: 200px; border: 1px dashed #aaa"></iframe>É
=head2 Features
Common features all modern DC clients (should) have:

View file

@ -13,12 +13,12 @@ POSIX-like environment with ncurses installed.
=item Latest version
1.12 ([dllink ncdu-1.12.tar.gz download]
1.13 ([dllink ncdu-1.13.tar.gz download]
- L<changes|https://dev.yorhel.nl/ncdu/changes>)
I also have convenient static binaries for Linux
L<i486|https://dev.yorhel.nl/download/ncdu-linux-i486-1.12.tar.gz> and
L<ARM|https://dev.yorhel.nl/download/ncdu-linux-arm-1.12.tar.gz>. Download,
L<i486|https://dev.yorhel.nl/download/ncdu-linux-i486-1.13.tar.gz> and
L<ARM|https://dev.yorhel.nl/download/ncdu-linux-arm-1.13.tar.gz>. Download,
extract and run; no compilation or installation necessary (uses
L<musl|http://www.musl-libc.org/>).
@ -65,12 +65,16 @@ L<Open Build Service|https://build.opensuse.org/package/show?package=ncdu&projec
Packages for NetBSD, DragonFlyBSD, MirBSD and others can be found on
L<pkgsrc|http://pkgsrc.se/sysutils/ncdu>.
A port to z/OS is available L<here|https://dovetail.com/community/ncdu.html>.
=head2 Similar projects
=over
=item L<Duc|http://duc.zevv.nl/> - Multiple user interfaces.
=item L<gt5|http://gt5.sourceforge.net/> - Quite similar to ncdu, but a different approach.
=item L<tdu|http://webonastick.com/tdu/> - Another small ncurses-based disk usage visualization utility.

View file

@ -1,3 +1,12 @@
1.13 - 2018-01-29
- Add "extended information" mode and -e flag
- Add file mode, modification time and uid/gid to info window with -e
- Add experimental color support and --color flag
- Add -rr option to disable shell spawning
- Remove directory nesting limit on file import
- Fix handling of interrupts during file import
- Fix undefined behavior that triggered crash on OS X
1.12 - 2016-08-24
- Add NCDU_SHELL environment variable
- Add --confirm-quit flag

View file

@ -19,17 +19,18 @@ object is an array:
=head2 Versioning
The C<< <majorver> >> and C<< <minorver> >> elements indicate the version of the file
format. These are numbers with accepted values in the range of
C<< 0 <= version <= 10000 >>. Major version must be C<1>, minor version is currently C<0>. The
major version should increase if backwards-incompatible changes are made
(preferably never), the minor version can be increased to indicate additions to
the existing format.
The C<< <majorver> >> and C<< <minorver> >> elements indicate the version of
the file format. These are numbers with accepted values in the range of C<< 0
<= version <= 10000 >>. Major version must be C<1>. Minor version is C<0> for
ncdu 1.9 till 1.12, and C<1> since ncdu 1.13 for the addition of the extended
mode. The major version should increase if backwards-incompatible changes are
made (preferably never), the minor version can be increased to indicate
additions to the existing format.
=head2 Metadata
The C<< <metadata> >> element is a JSON object holding whatever (short)
metadata you'd want. This block is currently (1.9-1.12) ignored by ncdu when
metadata you'd want. This block is currently (1.9-1.13) ignored by ncdu when
importing, but it writes out the following keys when exporting:
=over
@ -109,7 +110,7 @@ C<< 0 <= dev < 2^64 >>.
Number. Inode number as reported by C<lstat().st_ino>. Together with the Device
ID this uniquely identifies a file in this dump. In the case of hard links, two
objects may appear with the same (C<dev>,C<ino>) combination. A value of 0 is
assumed if this field is absent. This is currently (ncdu 1.9-1.12) not a
assumed if this field is absent. This is currently (ncdu 1.9-1.13) not a
problem as long as the C<hlnkc> field is false, otherwise it will consider
everything with the same C<dev> and empty C<ino> values as a single hardlinked
file. Accepted values are in the range of C<< 0 <= ino < 2^64 >>.
@ -163,6 +164,37 @@ whatever else your system may support. If absent, C<false> is assumed.
=back
=head3 Extended information
In addition, the following fields are exported when I<extended information>
mode is enabled (available since ncdu 1.13). See the C<-e> flag in L<ncdu(1)>
for details.
=over
=item uid
Number, user ID who owns the file. Accepted values are in the range
C<< 0 <= uid < 2^31 >>.
=item gid
Number, group ID who owns the file. Accepted values are in the range
C<< 0 <= uid < 2^31 >>.
=item mode
Number, the raw file mode as returned by L<lstat(3)>. For Linux systems, see
L<inode(7)> for the interpretation of this field. Accepted range:
C<< 0 <= mode < 2^16 >>.
=item mtime
Number, last modification time as a UNIX timestamp. Accepted range:
C<< 0 <= mtime < 2^64 >>.
=back
=head2 Miscellaneous notes
As mentioned above, file/directory names are B<not> converted to any specific

View file

@ -35,8 +35,8 @@ I<FILE> is equivalent to C<->, the file is read from standard input.
For the sake of preventing a screw-up, the current version of ncdu will assume
that the directory information in the imported file does not represent the
filesystem on which the file is being imported. That is, the refresh and file
deletion options in the browser will be disabled.
filesystem on which the file is being imported. That is, the refresh, file
deletion and shell spawning options in the browser will be disabled.
=item I<dir>
@ -54,6 +54,19 @@ directory with many files. 10.000 files will get you an export in the order of
gzip. This scales linearly, so be prepared to handle a few tens of megabytes
when dealing with millions of files.
=item -e
Enable extended information mode. This will, in addition to the usual file
information, also read the ownership, permissions and last modification time
for each file. This will result in higher memory usage (by roughly ~30%) and in
a larger output file when exporting.
When using the file export/import function, this flag will need to be added
both when exporting (to make sure the information is added to the export), and
when importing (to read this extra information in memory). This flag has no
effect when importing a file that has been exported without the extended
information.
=back
=head2 Interface options
@ -97,6 +110,16 @@ option has no effect when C<-o> is used, because there will not be a browser
interface in that case. It has no effect when C<-f> is used, either, because
the deletion feature is disabled in that case anyway.
WARNING: This option will only prevent deletion through the file browser. It is
still possible to spawn a shell from ncdu and delete or modify files from
there. To disable that feature as well, pass the C<-r> option twice (see
C<-rr>).
=item -rr
In addition to C<-r>, this will also disable the shell spawning feature of the
file browser.
=item --si
List sizes using base 10 prefixes, that is, powers of 1000 (KB, MB, etc), as
@ -108,6 +131,12 @@ prefixes, that is, powers of 1024 (KiB, MiB, etc).
Requires a confirmation before quitting ncdu. Very helpful when you
accidentally press 'q' during or after a very long scan.
=item --color I<SCHEME>
Select a color scheme. Currently only two schemes are recognized: I<off> to
disable colors (the default) and I<dark> for a color scheme intended for dark
backgrounds.
=back
=head2 Scan Options

View file

@ -1,29 +1,29 @@
=pod
Note: While these screenshots are from version 1.7, the latest version has only
little visible changes. Let me also apologize for the crappy formatting, I
should take some smaller shots next time...
These screenshots were made with ncdu 1.13 with the C<--color=dark> option.
Colors are not available in older versions and (in 1.13) still disabled by
default.
=head2 Scanning...
[img scr ncduscan.png Ncdu scanning a large directory.]
[img scr ncduscan-2.png Ncdu scanning a large directory.]
=head2 Done scanning
[img scr ncdudone.png Ncdu done scanning a large directory.]
[img scr ncdudone-2.png Ncdu done scanning a large directory.]
=head2 Directory information
[img scr ncduinfo.png Ncdu displaying directory information.]
[img scr ncduinfo-2.png Ncdu displaying directory information.]
=head2 Delete confirmation
[img scr ncduconfirm.png Ncdu asking for confirmation to delete a file.]
[img scr ncduconfirm-2.png Ncdu asking for confirmation to delete a file.]
=head2 Help screen
[img scr ncduhelp1.png Ncdu help screen.]
[img scr ncduhelp1-2.png Ncdu help screen.]
=head2 About screen
[img scr ncduhelp2.png Ncdu about screen.]
[img scr ncduhelp2-2.png Ncdu about screen.]

54
dat/nginx-confgen Normal file
View file

@ -0,0 +1,54 @@
=pod
nginx-confgen is a simple preprocessor and macro system for
L<nginx|http://nginx.org/> and nginx-like configuration files. It support
variable substitution, macro expansion and using the output of arbitrary
commands to generate config files.
=head2 Example
pre_set $certdir /etc/nginx-certificates/;
# Fetch the 'resolver' from /etc/resolv.conf
pre_exec $nameserver "grep nameserver /etc/resolv.conf \\
| head -n 1 | sed 's/^nameserver //'";
resolver $nameserver;
# Convenient macro to create a HTTPS virtual host
macro vhost $domain @aliases &block {
server {
listen [::]:443 ssl;
server_name $domain @aliases;
ssl_certificate $certdir/$domain/fullchain.pem;
ssl_certificate_key $certdir/$domain/privkey.pem;
pre_if -f $certdir/$domain/ocsp.der {
ssl_stapling_file $certdir/$domain/ocsp.der;
}
&block;
}
}
vhost example.com www.example.com {
root /var/www/example.com;
}
See the L<manual|https://dev.yorhel.nl/nginx-confgen/man> for more features.
=head2 Download
If you're on a x86_64 Linux system, you can simply use the binary:
curl -s https://dev.yorhel.nl/download/nginx-confgen-linux-amd64-1.1.tar.gz | tar -xzf-
./nginx-confgen <input.conf >output.conf
To compile from source, install L<Haskell Stack|https://haskellstack.org/> and run:
git clone git://g.blicky.net/nginx-confgen.git
cd nginx-confgen
stack install
The git repository is also available for L<online
browsing|https://g.blicky.net/nginx-confgen.git/>.

View file

@ -0,0 +1,8 @@
1.1 - 2018-01-24
- Add pre_warn directive
- Add -i/-o/-v/-h command line arguments
- Add support for custom pre_include search paths (-I flag)
- Fix handling of some common custom block directives (e.g. 'types')
1.0 - 2018-01-19
- Initial version

285
dat/nginx-confgen-man Normal file
View file

@ -0,0 +1,285 @@
=pod
=head1 NAME
nginx-confgen - A preprocessor and macro system for nginx(-like) configuration
files.
=head1 SYNOPSIS
nginx-confgen -i input.conf -o output.conf
=head1 DESCRIPTION
nginx-confgen can be used to do pre-processing for nginx configuration files
(and other configuration files with a similar syntax). It has support for
"compile-time" macro expansion and variable interpolation, which should make it
less tedious to maintain large and complex configurations.
nginx-confgen does not currently support any command-line arguments. It simply
reads the configuration from standard input, and writes the processed
configuration to standard output.
nginx-confgen works by parsing the input into a syntax tree, modifying this
tree, and then formatting the tree to generate the output. It is completely
oblivious to nginx contexts and directives, so it is possible to do nonsensical
transformations and generate incorrect configuration files. Comments in the
input file will not be present in the output. See also the L</BUGS & WARTS>
below.
B<WARNING:> Do NOT use nginx-confgen with untrusted input, the C<pre_exec>
directive allows, by design, arbitrary code execution.
=head1 OPTIONS
The following command-line options are supported:
=over
=item -h
Show help text.
=item -V, --version
Show program version.
=item -i I<FILE>
Use the given file name as input file. If this option is not given or set to
C<->, then the file will be read from standard input.
=item -o I<FILE>
Write the output to the given file. If this option is not given or set to C<->,
then the file will be written to standard output.
=item -I I<DIR>
Set the search path for I<pre_include> directives. This option can be given
multiple times to search several directories in order. If this option is not
given, then include files are resolved relative to the directory that
nginx-confgen is run from (i.e. C<-I .>).
=back
=head1 DIRECTIVES
nginx-confgen recognizes and interprets the following directives:
=head2 pre_include
Similar to the C<include> directive in nginx, except that the file is included
during preprocessing. The included file may contain any preprocessing
directives supported by nginx-confgen. Variables and macros defined in the
included file will be available in the parent file.
Relative paths are searched for in the directories given with the C<-I> flag.
=head2 pre_set
Similar to the C<set> directive in nginx, except that variables defined with
C<pre_set> are resolved during preprocessing. Note that variables defined with
C<pre_set> are only available in the same scope as they are defined, for
example:
pre_set $var outer;
location / {
pre_set $var inner;
# $var is now "inner" within this location block.
}
# $var is "outer" again after the location block.
(This may change in the future)
=head2 pre_exec
Run a shell command, and store the output in a variable. For example, nginx
will not use your system's DNS resolution methods to resolve domain names.
Instead you need to manually set a C<resolver> address. With the following hack
you can fetch the nameserver from C</etc/resolv.conf> and use that as the
C<resolver>:
pre_exec $nameserver "grep nameserver /etc/resolv.conf \\
| head -n 1 | sed 's/^nameserver //'";
resolver $nameserver;
(The C<\\> is necessary, otherwise your shell will consider the newline as a
new command).
=head2 pre_if
Similar to the C<if> directive in nginx, except that this is evaluated during
preprocessing. nginx-confgen has a few warts with regards to parenthesis,
things usually work better without:
pre_if -f $certdir/ocsp.der {
ssl_stapling on;
ssl_stapling_file $certdir/ocsp.der;
}
pre_if !-f $certdir/ocsp.der {
ssl_stapling off;
}
# You can have different configuration depending on the name of
# the system on which nginx-confgen runs. Like... yeah.
pre_exec $hostname 'hostname';
pre_if $hostname ~* ^proxy_for_(.+) {
proxy_pass http://$1/;
}
=head2 pre_warn
This directive, when interpreted, will generate a warning to the standard error
of nginx-confgen. Can be used to signal that a special configuration is being
used:
pre_if -e /etc/offline-mode {
pre_warn "Putting website in offline mode!";
}
Or to warn about certain directives:
pre_macro proxy_cache $var {
pre_warn "Using proxy_cache with $var violates company policy!";
# But we can output it anyway.
proxy_cache $var;
}
=head2 macro
Define a I<macro>, which is a configuration block that you can later refer to.
The general syntax is as follows:
macro macro_name $var1 $var2 @remaining_vars &block_var {
# contents
}
The optional C<@remaining_vars> argument will capture any number of variables,
and can be passed to another directive inside the macro contents. The optional
C<&block_var> allows the macro to be invoked with a block argument, which will
expand to any number of directives. Some examples:
macro le {
location /.well-known/acme-challenge {
alias /etc/letsencrypt/challenge;
}
}
# Usage:
le;
macro redir $path $to {
location $path {
return 301 $to;
}
}
# Usage:
redir / http://blicky.net/;
macro vhost $primary_name @aliases &block {
server {
listen [::]:443 ssl;
server_name $primary_name @aliases;
ssl_certificate $crtdir/$primary_name/fullchain.pem;
ssl_certificate_key $crtdir/$primary_name/privkey.pem;
&block;
}
}
# Usage:
vhost example.com {
root /var/www/example.com;
}
vhost example.org alias.example.org {
root /var/www/example.org;
}
Note that these are I<hygienic> macros, so variable capture is predictable (but
not necessarily the most useful):
pre_var $dest /a;
macro redir {
# This will be /a, regardless of the context in which this macro is called.
return 301 $dest;
}
# $dest is still '/a' inside the macro after this new variable definition.
pre_var $dest /b;
redir;
Similarly, macro arguments will not be available inside C<&block> expansion or
nested macro expansion.
=head1 BUGS & WARTS
nginx-confgen is a quickly written hack to solve a particular use case, it is
quite likely to have some weird behavior and bugs. Here's a few I am aware of:
=over
=item *
The nginx configuration syntax is not as regular as I had hoped. It's possible
for nginx modules to extend the syntax somewhat. A good example is the I<types>
directive in I<ngx_http_core_module>. While nginx-confgen should be able to
handle the I<types> directive just fine, other extensions may cause syntax
errors or will not survive a round-trip through nginx-confgen.
This applies to all I<*_by_lua_block> directives in the I<ngx_http_lua_module>.
The I<_by_lua> directives that accept a string should work just fine.
=item *
Be careful with parenthesis around if statements, e.g.:
if ($a == $b) { }
Will get converted into:
if "(${a}" == "${b})" { }
Which is unlikely what you want. As a workaround, add some spaces:
if ( $a == $b ) { }
=item *
Arguments to directives may get reformatted, especially if they contain a
variable. This I<should> not matter in most cases, but in some particular
scenarios it does. Here's a few examples of reformatting:
return 301 http://blicky.net$request_uri;
# becomes:
return 301 "http://blicky.net${request_uri}";
add_header Something "${header}";
# becomes:
add_header Something $header;
This reformatting may cause different behavior for nginx directives that do not
support variable interpolation, such as C<error_log>.
=item *
C<pre_if> does not like empty strings, e.g.
pre_if $x == "" { }
Will throw an error, use the following instead:
pre_if $x { }
=item *
The error messages aren't always helpful.
=back
=head1 AUTHOR
nginx-confgen is written by Yoran Heling <projects@yorhel.nl>
Web: L<https://dev.yorhel.nl/nginx-confgen>

View file

@ -39,10 +39,10 @@ information and details.
=head2 Download
B<Latest packaged version:> 1.0 ([dllink TUWF-1.0.tar.gz download]
B<Latest packaged version:> 1.1 ([dllink TUWF-1.1.tar.gz download]
- L<CPAN mirror|http://search.cpan.org/dist/TUWF/>)
TUWF is also available on a git repository at L<http://g.blicky.net/tuwf.git/>.
TUWF is also available on a git repository at L<https://g.blicky.net/tuwf.git/>.
=head2 Websites using TUWF
@ -51,13 +51,13 @@ TUWF is also available on a git repository at L<http://g.blicky.net/tuwf.git/>.
=over
=item * L<VNDB.org|http://vndb.org/> (the site that spawned TUWF - L<open source|http://g.blicky.net/vndb.git/>)
=item * L<VNDB.org|https://vndb.org/> (the site that spawned TUWF - L<open source|https://g.blicky.net/vndb.git/>)
=item * L<Manned.org|http://manned.org/> (L<open source|http://g.blicky.net/manned.git/>)
=item * L<Manned.org|https://manned.org/> (L<open source|https://g.blicky.net/manned.git/>)
=item * L<This website|https://dev.yorhel.nl/> (L<open source|http://g.blicky.net/yorhel-dev.git/tree/index.cgi>)
=item * L<This website|https://dev.yorhel.nl/> (L<open source|https://g.blicky.net/yorhel-dev.git/tree/index.cgi>)
=item * L<Blicky.net Pastebin|http://p.blicky.net/> (L<open source|http://g.blicky.net/bpaste.git/tree/index.cgi>)
=item * L<Blicky.net Pastebin|https://p.blicky.net/> (L<open source|https://g.blicky.net/bpaste.git/tree/index.cgi>)
=item * The website embedded in the L<D&R Axum|http://www.d-r.nl/axum.html> mixing console.

View file

@ -1,3 +1,10 @@
1.1 - 2017-11-26
- Disallow exclamation mark in email address validation
- Add reqProtocol() method
- Add reqFCGI() method
- Remove 'X-Powered-By' header
- Fix handling of space character in load_recursive()
1.0 - 2015-09-17
- !! Some backwards-imcompatible changes, marked * !!
- kv_validate() improvements:

View file

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdEYNMrgIEOupr6LpYjlMaYwnOfoFAloaeBgACgkQYjlMaYwn
OfqtFRAA0ZXztAZ2GDb1puNXknJO1kDFam2DXUHXQvuNW9rn2I7nH0utdW/VWvt/
vpWAyKT8kvywpK7m1JGjVj8RiE/sgncTpYi3E3H+Dv6OO0z0dEoq6VUS3Mc5Gk1b
1TclOZt3uShK9PgjGsjJZcyMGvvgXMUO/5peBlbXDgWhokfVWZrM5r58Ve+dJwQn
4s5IDjoIqZy7RuSzlhkdT661NftfWpvjvr+XMmS3kdBsiIxTUrbPnUp1T85yYslA
0UpMCnGIwYdbu1J3igAMRIRmp0ix7e4ANagh97MVeCueO6acHAYqe3uui5SHwE+7
iuHWmyZTQtuikrZjpNBDMbQxUwk79SQaYOM5bXDn/pTTAcBm1RU2Sf3Slnk+Xlrk
kKjqgvf7qfx3zZKNMuTvxslzF4HUbE4/CB9I6Ccz4KnQ6qkyQMMPeo4ETjumCpPa
5CosdSFF4vrPHO7v6KTc2Q+l5gp4900Qb/wxydUptSJTTsCFNNRMwpkDwtSh9wfn
rU9L6vx54aem3nucqStFwe3gIkuu9NNqGCwpUu9Dga0/gnxP5i6O/bNC8MH3tnsR
yTodqjGQWsTtmpDaLnZX1TeCWCgjtVvfbrTqTUiM8l2BQnTVtgBJbGZDHnC1nCs0
/MnIEXxfLqrWynPieeAKSuxs2zrToR9PFDuWSVs6X0oCuo8SaUI=
=9nQv
-----END PGP SIGNATURE-----

View file

@ -0,0 +1 @@
4629606ce88c28e2ea767bf7b356fe9c TUWF-1.1.tar.gz

View file

@ -0,0 +1 @@
7f2c4018674f7009db5d3d30218d379210c3a972 TUWF-1.1.tar.gz

166
download/code/awshrink Normal file
View file

@ -0,0 +1,166 @@
#!/usr/bin/perl
# awshrink v0.1
# 2007-01-15
# Yoran Heling
# License: MIT
use strict;
use warnings;
use bytes;
# hash for easy lookup
my %shrinkable = ( map { $_ => 1 } qw|
DOMAIN LOGIN ROBOT WORMS EMAILSENDER EMAILRECEIVER
BROWSER UNKNOWNREFERER UNKOWNREFERERBROWSER SEREFERRALS
PAGEREFS SEARCHWORDS KEYWORDS SIDER_404 SIDER VISITOR
UNKNOWNREFERERBROWSER
|);
sub getstats { # read
my $fl = shift;
my @l;
seek($fl, 0, 0);
# get positions
while(<$fl>) {
last if /^END_MAP/;
next if !/^POS_([A-Z0-9_]+)\s+([0-9]+)\s+$/;
push(@l, [ $1, $2, 0, 0 ]);
}
# get sizes (bytes)
@l = sort { $a->[1] <=> $b->[1] } @l;
$l[$_][2] = ($l[$_+1] ? $l[$_+1][1] : -s $fl) - $l[$_][1]
for (0..$#l);
# get lines
for (0..$#l) {
seek $fl, $l[$_][1], 0;
<$fl> =~ /BEGIN_[A-Z0-9_]+\s+([0-9]+)/;
$l[$_][3] = $1;
}
print " Section Size (Bytes) Lines\n";
printf "%22s %12d %7d\n", $_->[0].($shrinkable{$_->[0]}?' ':'*'), $_->[2], $_->[3]
for (sort { $a->[2] <=> $b->[2] } @l);
print "* = not shrinkable\n";
}
sub fixmap { # read, write
my($fl, $FL) = @_;
my $map = '';
my @l;
seek $fl, 0, 0;
while(<$fl>) {
next if !/^BEGIN_([A-Z0-9_]+)/ || $1 eq 'MAP';
$map .= sprintf "POS_%s %-20d\n", $1, tell($fl)-length($_);
}
seek $fl, 0, 0;
seek $FL, 0, 0;
my $inmap = 0;
while(<$fl>) {
if(!$inmap && !/^BEGIN_MAP/) {
print $FL $_;
next;
}
$inmap = 1;
if(/^END_MAP/) {
printf $FL "BEGIN_MAP 27\n%sEND_MAP\n", $map;
$inmap = 0;
}
}
}
sub shrink { # read, write, %sections->{ key = section, value = lines }
my($fh, $FH, %sec) = @_;
seek $fh, 0, 0;
seek $FH, 0, 0;
while(<$fh>) {
if(!/^BEGIN_([A-Z0-9_]+)\s+([0-9]+)/ || !$sec{$1} || $sec{$1} >= $2) {
print $FH $_;
next;
}
my $sec = $1;
# read entire section
my @l;
while(<$fh>) {
last if /^END_/;
s/^[\s\r\n]+//;
s/[\s\r\n]+$//;
push @l, [ split / / ];
}
# sort section
# DOMAIN, LOGIN, ROBOT, WORMS, EMAILSENDER. EMAILRECEIVER,
# BROWSER, UNKNOWNREFERER, UNKOWNREFERERBROWSER, SEREFERRALS,
# PAGEREFS, SEARCHWORDS, KEYWORDS, SIDER_404, SIDER -> 1
# VISITOR -> 2
if($sec{$sec} > 10) { # assume sorted if we only want the first ten
@l = sort { $b->[1] <=> $a->[1] } @l if $sec ne 'VISITOR';
@l = sort { $b->[2] <=> $a->[2] } @l if $sec eq 'VISITOR';
}
# write section
printf $FH "BEGIN_%s %d\n", $sec, $sec{$sec};
print $FH join(' ', @{$l[$_]})."\n"
for (0..($sec{$sec}-1));
printf $FH "END_%s\n", $sec;
}
1;
}
sub run {
print "Usage: $@ [options] filename\n" and exit if !@ARGV;
my $file = pop;
my $ci = 0;
my $st = 0;
my %options;
while(local $_ = shift) {
/^-c$/ ? $ci++ :
/^-s$/ ? $st++ :
/^-([A-Za-z0-9_]+)$/ && $shrinkable{ uc($1) } ? $options{ uc($1) } = shift :
printf("Unknown option: %s\n", $_) && exit;
}
(!$options{$_} || $options{$_} !~ /^[0-9]+$/) && printf("Invalid value for -%s\n", $_) && exit
for keys %options;
print "Nothing to do...\n" and exit if !keys %options && !$st;
open(my $fl, '<', $file) || die $!;
my $tmpfile;
open(my $TMP, '+>', \$tmpfile);
open(my $FL, '+>', "$file~") || die $! if keys %options;
shrink($fl, $TMP, %options) && fixmap($TMP, $FL) if keys %options;
getstats(keys %options ? $FL : $fl) if $st;
close $fl;
close $TMP;
close $FL if keys %options;
rename "$file~", $file if $ci;
}
run @ARGV;

330
download/code/bbcode.c Normal file
View file

@ -0,0 +1,330 @@
/*
* BBCode 2 HTML converter by //YorHel
* Copyright 2006 Y.Heling,
* License: MIT
*
* Created just to learn C, probably very ugly piece of code
* and probably with a _LOT_ of bugs... But the only way to
* learn a programming language is to code something yourself :)
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_ARGH_SIZE 500
#define MAX_TAG_SIZE 10
#define MAX_NESTED_TAGS 100
#define TAGCHARS 28
#define NUMBER_OF_TAGS 16
#define TRUE 1
#define FALSE 0
/* typedefs */
typedef enum {
B,
I,
U,
SIZE,
COLOR,
URL,
QUOTE,
QUOTE2,
IMG,
IMG2,
EMAIL,
LIST,
LIST2,
CODE,
HTML,
UNDEF, /* hack */
} TAGNAME;
typedef struct {
TAGNAME intags[MAX_NESTED_TAGS];
int curtag;
char tag[MAX_TAG_SIZE];
char argh[MAX_ARGH_SIZE];
FILE *dest;
int inlist;
int liststart;
} PARSEINFO; /* just a hack to prevent the use of global variables */
typedef struct {
char *bb;
char *html_s;
char *html_e;
char arg;
} TAGINFO; /* store global information about the tags */
/* functions */
void parsebbcode(FILE *, FILE *);
void converttag(PARSEINFO *);
void formattag(PARSEINFO *);
char endtags(PARSEINFO *, TAGNAME);
void convertchars(PARSEINFO *, const char *);
void convertchar(PARSEINFO *, const int);
void convertchararg(char *, int);
char istagchar(const int);
void err(const char *);
/* global vars (only consts!) */
const char tagchars[TAGCHARS] = "abcdefghijklmnopqrstuvwxyz/*";
const TAGINFO taglist[NUMBER_OF_TAGS] = { /* same order as TAGNAME! */
{ "b", "<b>", "</b>", FALSE },
{ "i", "<i>", "</i>", FALSE },
{ "u", "<span style=\"text-decoration: underline\">", "</span>", FALSE },
{ "size", "<span style=\"font-size: %spx\">", "</span>", TRUE },
{ "color", "<span style=\"color: %s\">", "</span>", TRUE },
{ "url", "<a href=\"%s\">", "</a>", TRUE },
{ "quote", "<span class=\"bbcode_quote_header\">Quote: <span class=\"bbcode_quote_body\">", "</span></span>", FALSE },
{ "quote", "<span class=\"bbcode_quote_header\">%s wrote: <span class=\"bbcode_quote_body\">", "</span></span>", TRUE },
{ "img", "<img src=\"", "\" alt=\"\" />", FALSE },
{ "img", "<img src=\"%s\" alt=\"", "\" />", TRUE },
{ "email", "<a href=\"mailto:%s\">", "</a>", TRUE },
{ "list", "<ul>", "</li></ul>", FALSE },
{ "list", "<ul style=\"list-style-type: %s\">", "</li></ul>", TRUE },
{ "code", "<span class=\"bbcode_code_header\">Code: <span class=\"bbcode_code_body\">", "</span></span>", FALSE },
{ "html", "", "", FALSE },
{ "", "", "", FALSE },
}; /* NOTE: not all characteristics of the BBCodes are defined above, there is also a lot of hard-coded stuff below for a few tags */
int main() {
/* printf("BBCode to HTML converter by //YorHel\n");
printf("Copyright 2006 Y. Heling\n\n");
printf("* Reading & parsing bbcode...\n");
while(1) {
FILE *file;
if((file = fopen(BBFILE, "r")) == NULL) err("Couldn't open BBFILE");
parsebbcode(file, stdout);
fclose(file);
}
printf("\n");*/
parsebbcode(stdin, stdout);
return 0;
}
void parsebbcode(FILE *file, FILE *dest) {
PARSEINFO pitemp; /* make sure we allocate the memory */
PARSEINFO *pi = &pitemp; /* but we are still going to use a pointer */
char intag = pi->inlist = pi->liststart = 0;
pi->curtag = 1;
pi->intags[0] = UNDEF;
sprintf(pi->tag, "");
sprintf(pi->argh, "");
pi->dest = dest;
int c;
char tmp[MAX_TAG_SIZE+MAX_ARGH_SIZE+4]; /* temp string, should be able to hold anything necessary */
while((c = fgetc(file)) && c != EOF) {
if(intag == 0 && c == '[') {
intag = 1;
}
else if(intag == 1 && c == '=')
intag = -1;
else if(intag != 0 && c == ']') {
intag = 0;
converttag(pi);
sprintf(pi->tag, "");
sprintf(pi->argh, "");
}
else if(intag == 1 && strlen(pi->tag) < sizeof(pi->tag)) {
if(istagchar(c)) {
sprintf(tmp, "%c", tolower(c));
strcat(pi->tag, tmp);
} else {
sprintf(tmp, "[%s", pi->tag);
convertchars(pi, tmp);
sprintf(pi->tag, "");
}
}
else if(intag == 1) {
intag = 0;
sprintf(tmp, "[%s", pi->tag);
convertchars(pi, tmp);
sprintf(pi->tag, "");
}
else if(intag == -1 && (strlen(pi->argh)+10) < sizeof(pi->argh)) {
convertchararg(tmp, c);
strcat(pi->argh, tmp);
}
else if(intag == -1) {
sprintf(tmp, "[%s=%s", pi->tag, pi->argh);
convertchars(pi, tmp);
sprintf(pi->tag, "");
sprintf(pi->argh, "");
intag = 0;
}
else
convertchar(pi, c);
}
/* some stuff left in the buffer */
if(strlen(pi->tag) > 0 && strlen(pi->argh) == 0)
fprintf(dest, "[%s", pi->tag);
else if(strlen(pi->tag) > 0 && strlen(pi->argh) > 0)
fprintf(dest, "[%s=%s", pi->tag, pi->argh);
/* automatically close opened tags */
endtags(pi, UNDEF);
}
void converttag(PARSEINFO *pi) {
/* ignore tag if we're not allowed to nest */
if((pi->intags[pi->curtag] == CODE && strcmp(pi->tag, "/code") != 0)
|| (pi->intags[pi->curtag] == HTML && strcmp(pi->tag, "/html") != 0)
|| ((pi->intags[pi->curtag] == IMG || pi->intags[pi->curtag] == IMG2) && strcmp(pi->tag, "/img"))) {
formattag(pi);
return;
}
/* parse list items */
if(!strcmp(pi->tag, "*") && pi->inlist) {
endtags(pi, LIST);
if(pi->inlist == pi->liststart)
fprintf(pi->dest, "</li>");
else
pi->liststart = pi->inlist;
fprintf(pi->dest, "<li>");
return; /* no need to parse more */
}
/* begin a tag */
if(*pi->tag != '/') {
int i; int got = -1;
for(i=0 ; i<NUMBER_OF_TAGS && got == -1 ; i++)
if(!strcmp(pi->tag, taglist[i].bb)) {
got = i;
if(strlen(pi->argh) == 0 && !taglist[i].arg)
fprintf(pi->dest, "%s", taglist[i].html_s);
else if(strlen(pi->argh) > 0 && taglist[i].arg) {
if(i != LIST2)
fprintf(pi->dest, taglist[i].html_s, pi->argh);
else
fprintf(pi->dest, taglist[i].html_s, strcmp(pi->argh, "1") ? "lower-roman" : "decimal");
}
else
got = -1;
};
if(got != -1) {
pi->intags[++pi->curtag] = got;
if(got == LIST || got == LIST2)
pi->inlist++;
}
else
formattag(pi);
return;
}
/* end a tag */
else {
char *tag = pi->tag+1; /* strip the '/' */
int i;
char got = FALSE;
for(i=0 ; i<NUMBER_OF_TAGS && got == FALSE ; i++)
if(!strcmp(tag, taglist[i].bb) && endtags(pi, i)) {
fprintf(pi->dest, "%s", taglist[i].html_e);
if(pi->intags[pi->curtag] == LIST || pi->intags[pi->curtag] == LIST2)
pi->liststart = --pi->inlist;
pi->curtag--;
got = TRUE;
};
if(!got)
formattag(pi);
}
}
char endtags(PARSEINFO *pi, TAGNAME to) {
char s = FALSE;
if(to == QUOTE || to == LIST || to == IMG)
s = TRUE;
else if(to == QUOTE2 || to == LIST2 || to == IMG2) {
s = TRUE;
to--;
}
int i = pi->curtag;
if(to != UNDEF)
while(pi->intags[i] != to && (!s || pi->intags[i] != to+1) && i > 0)
i--;
if(i) {
while(pi->intags[pi->curtag] != to && (!s || pi->intags[pi->curtag] != (to+1)) && pi->curtag > 0) {
if(pi->intags[pi->curtag] == LIST || pi->intags[pi->curtag] == LIST2)
pi->liststart = --pi->inlist;
fprintf(pi->dest, "%s", taglist[pi->intags[pi->curtag--]].html_e);
}
return TRUE;
}
else
return FALSE;
}
void formattag(PARSEINFO *pi) {
char tmp[MAX_TAG_SIZE+MAX_ARGH_SIZE+4];
if(strlen(pi->argh) == 0)
sprintf(tmp, "[%s]", pi->tag);
else
sprintf(tmp, "[%s=%s]", pi->tag, pi->argh);
convertchars(pi, tmp);
}
void convertchars(PARSEINFO *pi, const char *chars) {
int i;
for(i=0;i<strlen(chars);i++)
convertchar(pi, *(chars+i));
}
void convertchar(PARSEINFO *pi, const int c) {
if(pi->inlist && pi->inlist != pi->liststart)
return;
else if(pi->intags[pi->curtag] == HTML)
fprintf(pi->dest, "%c", c);
else if(pi->intags[pi->curtag] == IMG || pi->intags[pi->curtag] == IMG2) {
char tmp[10];
convertchararg(tmp, c);
fprintf(pi->dest, tmp);
}
else if(c == '\n')
fprintf(pi->dest, "<br />\n");
else if(c == '&')
fprintf(pi->dest, "&amp;");
else if(c == '<')
fprintf(pi->dest, "&lt;");
else if(c == '>')
fprintf(pi->dest, "&gt;");
else
fprintf(pi->dest, "%c", c);
}
void convertchararg(char *to, const int c) {
switch(c) {
case '\n' :
sprintf(to, ""); break;
case '&' :
sprintf(to, "&amp;"); break;
case '"' :
sprintf(to, "&quot;"); break;
default :
sprintf(to, "%c", c);
}
}
char istagchar(const int c) {
int i;
int c2 = tolower(c);
for(i=0;i<TAGCHARS;i++)
if(c2 == *(tagchars+i))
return TRUE;
return FALSE;
}
void err(const char *msg) {
fprintf(stderr, "ERROR: %s\n", msg);
exit(1);
}

180
download/code/echoserv.c Normal file
View file

@ -0,0 +1,180 @@
/*
* echoserv.c - a simple single-threaded TCP server test
* by //YorHel
*
* Copyright 2006 Y. Heling,
* February 2006
* License: MIT
*
* Use it at your own risk
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#define LISTEN_PORT 1337
#define MAX(x,y) ((x) > (y) ? (x) : (y))
;
#define MAX_CONNECTIONS 10
#define READ_BUFFER_SIZE 512
#define CST_FREE 0
#define CST_READ 1
#define CST_WRITE 2
typedef struct {
int fd;
char state;
char *buf;
} connections;
static connections *conns;
void error(const char *);
void close_and_free(int);
int main() {
printf("Simple TCP server by //YorHel\n\n");
printf("* Creating socket\n");
int l;
if((l = socket(AF_INET, SOCK_STREAM, 0)) < 0)
error("Can't create socket");
printf("* Setting SO_REUSEADDR on main socket\n");
int set = 1;
if(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (void *) &set, sizeof(set)) < 0)
error("Can't set SO_REUSEADDR on main socket");
printf("* Binding socket\n");
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if(bind(l, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("Can't bind socket");
printf("* Listening to socket\n");
listen(l, 5);
printf("* Creating connections table\n");
conns = (connections *) malloc( sizeof(connections) * MAX_CONNECTIONS );
if(conns == (connections *) 0)
error("Can't allocate memory for connections table");
int cnum;
for(cnum=MAX_CONNECTIONS; cnum--;)
conns[cnum].state = CST_FREE;
printf("* Accepting connections\n");
/* re-use the same vars over-and-over again
* is a little faster than defining them each time */
int n, cn, newid, clifd;
char stop = 0;
fd_set rd, wr;
struct sockaddr_in cliaddr;
unsigned int clilen;
/* main loop */
while(!stop) {
/* set FDs for select() */
FD_ZERO(&rd); FD_ZERO(&wr);
n = 0;
FD_SET(l, &rd);
n = MAX(l, n);
/* set FDs for the connections */
for(cn=MAX_CONNECTIONS;cn--;) {
if(conns[cn].state == CST_READ) {
FD_SET(conns[cn].fd, &rd);
n = MAX(n, conns[cn].fd);
} else if(conns[cn].state == CST_WRITE) {
FD_SET(conns[cn].fd, &wr);
n = MAX(n, conns[cn].fd);
}
}
n = select(n + 1, &rd, &wr, (fd_set *) NULL, (struct timeval *) 0);
if(n < 0)
error("select() failed...");
if(n == 0)
continue;
/* Something happend, handle it :) */
if(FD_ISSET(l, &rd)) { /* new connection */
/* get free slot, if one */
newid = -1;
for(cn=MAX_CONNECTIONS;cn--;)
if(conns[cn].state == CST_FREE) {
newid = cn; break;
}
if(newid < 0)
printf(" *WARNING: Too many connections\n");
else {
/* accept the connection */
clilen = sizeof(cliaddr);
memset(&cliaddr, 0, clilen);
clifd = accept(l, (struct sockaddr *) &cliaddr, &clilen);
if(clifd < 0)
error("Can't accept connection");
printf(" [%d] We have a connection!!\n", newid);
conns[newid].state = CST_WRITE;
if((conns[newid].buf = malloc(READ_BUFFER_SIZE)) < 0)
error("Can't allocate memory");
sprintf(conns[newid].buf, "Hello world!\n");
conns[newid].fd = clifd;
}
}
/* checking active sockets */
for(cn=MAX_CONNECTIONS;cn--;) {
/* we can write */
if(conns[cn].state == CST_WRITE && FD_ISSET(conns[cn].fd, &wr)) {
if(write(conns[cn].fd, conns[cn].buf, strlen(conns[cn].buf)) <= 0)
close_and_free(cn);
printf(" [%d] Sent: %s", cn, conns[cn].buf);
conns[cn].state = CST_READ;
}
/* we can read */
if(conns[cn].state == CST_READ && FD_ISSET(conns[cn].fd, &rd)) {
memset((void *) conns[cn].buf, 0, READ_BUFFER_SIZE);
if(read(conns[cn].fd, conns[cn].buf, READ_BUFFER_SIZE) <= 0)
close_and_free(cn);
printf(" [%d] G0t: %s", cn, conns[cn].buf);
conns[cn].state = CST_WRITE;
if(strstr(conns[cn].buf, "exit") == conns[cn].buf) {
printf(" [%d] Closing connection\n", cn);
close_and_free(cn);
} else if(strstr(conns[cn].buf, "die()") == conns[cn].buf) {
printf("Got die() from connection #%d, dying!\n", cn);
stop = 1;
}
}
}
}
/* close all connections before dying */
for(cnum=MAX_CONNECTIONS;cnum--;)
if(conns[cnum].state != CST_FREE)
close_and_free(cnum);
close(l);
return 0;
}
void close_and_free(int cn) {
close(conns[cn].fd);
conns[cn].state = CST_FREE;
free(conns[cn].buf);
}
void error(const char *msg) {
perror(msg);
exit(1);
}

View file

@ -0,0 +1,248 @@
#!/usr/bin/perl
# grenamr v0.1
# 2008-08-01
# Yoran Heling
# License: MIT
use warnings;
use strict;
use Gtk2 -init;
our $VERSION = '0.1';
my %w;
my $m;
use constant {
C_OLD => 0,
C_NEW => 1,
C_FLG => 2,
F_REG => 0x01,
F_MAN => 0x02, # not implemented yet...
F_CON => 0x04,
};
create();
Gtk2->main;
sub create {
$w{win} = Gtk2::Window->new('toplevel');
$w{win}->signal_connect(destroy => \&close);
$w{win}->resize(500, 500);
$w{win}->move(300, 300);
$w{win}->set(title => 'GRenamR v'.$VERSION);
$w{vbGlobal} = Gtk2::VBox->new(0, 0);
$w{win}->add($w{vbGlobal});
$w{hbDir} = Gtk2::HBox->new(0, 0);
$w{vbGlobal}->pack_start($w{hbDir}, 0, 1, 0);
$w{lbDir} = Gtk2::Label->new('_Directory');
$w{hbDir}->pack_start($w{lbDir}, 0, 0, 5);
$w{fcDir} = Gtk2::FileChooserButton->new('Select a directory', 'select-folder');
$w{fcDir}->signal_connect('current-folder-changed', \&fetchdir);
$w{hbDir}->pack_start($w{fcDir}, 1, 1, 0);
$w{lbDir}->set(mnemonic_widget => $w{fcDir}, use_underline => 1);
$w{swFiles} = Gtk2::ScrolledWindow->new;
$w{swFiles}->set_policy('automatic', 'automatic');
$w{vbGlobal}->pack_start($w{swFiles}, 1, 1, 0);
$w{tvFiles} = Gtk2::TreeView->new;
$w{swFiles}->add($w{tvFiles});
$m = $w{lsFiles} = Gtk2::ListStore->new('Glib::String', 'Glib::String', 'Glib::Int');
$w{tvFiles}->set_model($w{lsFiles});
$w{tvFiles}->insert_column_with_data_func(0, '', Gtk2::CellRendererPixbuf->new, \&column_func, 0);
my $c = Gtk2::CellRendererText->new;
$w{tvFiles}->insert_column_with_data_func(1, 'Old', $c, \&column_func, 1);
$w{tvFiles}->insert_column_with_data_func(2, 'New', $c, \&column_func, 2);
$w{tvFiles}->get_column(1)->set(sizing => 'autosize');
$w{tvFiles}->get_column(2)->set(sizing => 'autosize');
$w{hbPerl} = Gtk2::HBox->new(0, 0);
$w{vbGlobal}->pack_start($w{hbPerl}, 0, 1, 0);
$w{lbPerl} = Gtk2::Label->new('_Expression');
$w{hbPerl}->pack_start($w{lbPerl}, 0, 0, 5);
$w{enPerl} = Gtk2::Entry->new;
$w{enPerl}->signal_connect('changed', \&applyfilter);
$w{hbPerl}->pack_start($w{enPerl}, 1, 1, 0);
$w{lbPerl}->set(mnemonic_widget => $w{enPerl}, use_underline => 1);
$w{imPerl} = Gtk2::Image->new_from_stock('gtk-yes', 'menu');
$w{hbPerl}->pack_start($w{imPerl}, 0, 1, 0);
$w{hbBottom} = Gtk2::HBox->new(0, 0);
$w{vbGlobal}->pack_start($w{hbBottom}, 0, 1, 0);
$w{alStatus} = Gtk2::Alignment->new(0,0.8,0,0);
$w{hbBottom}->pack_start($w{alStatus}, 1, 1, 5);
$w{lbStatus} = Gtk2::Label->new('');
$w{alStatus}->add($w{lbStatus});
$w{alBottom} = Gtk2::Alignment->new(1,1,0,1);
$w{hbBottom}->pack_start($w{alBottom}, 0, 1, 0);
$w{bbBottom} = Gtk2::HButtonBox->new;
$w{bbBottom}->set(spacing => 5);
$w{alBottom}->add($w{bbBottom});
$w{btApply} = Gtk2::Button->new('_Rename');
$w{btApply}->signal_connect('clicked', \&dorename);
$w{bbBottom}->pack_start($w{btApply}, 0, 0, 5);
$w{btClose} = Gtk2::Button->new('_Close');
$w{btClose}->signal_connect('clicked', \&close);
$w{bbBottom}->pack_start($w{btClose}, 0, 0, 5);
$w{win}->show_all;
$w{enPerl}->grab_focus;
if($ARGV[0] && -d $ARGV[0]) {
$w{fcDir}->set_current_folder($ARGV[0]);
} else {
fetchdir();
}
}
sub column_func {
my ($column, $cell, $model, $iter, $col) = @_;
my $flags = $model->get($iter, 2);
$cell->set(
$col > 0 ? (
text => $model->get($iter, $col-1),
) : (
'stock-id' => $flags & F_CON ? 'gtk-dialog-error'
: $flags & F_REG ? 'gtk-edit'
: $flags & F_MAN ? 'gtk-apply'
: 'gtk-file',
),
$flags & F_CON ? ( cell_background => '#ffcccc' ) :
$flags & F_REG ? ( cell_background => '#ccffcc' ) :
$flags & F_MAN ? ( cell_background => '#ffffcc' ) :
( cell_background_set => 0 ),
);
}
sub close {
Gtk2->main_quit;
}
sub fetchdir {
Gtk2->main_iteration_do(0);
my $d = $w{fcDir}->get_current_folder;
$w{lbStatus}->set(label => 'Loading...');
my @lst;
opendir(my $D, $d)
|| return $w{lbStatus}->set(label => 'Unable to open the selected directory');
-f $d.'/'.$_ and push @lst, $_ for (readdir $D);
closedir $D;
$m->clear;
$m->set($m->append, C_OLD, $_, C_NEW, $_, C_FLG, 0)
for (sort @lst);
$w{lbStatus}->set(label => sprintf '%d files found.', scalar @lst);
applyfilter();
}
sub applyfilter {
my $p = $w{enPerl}->get_text;
# create a list of files
my @list;
my $i = $m->get_iter_first;
do {
push @list, $m->get($i, C_OLD)
} while ($i = $m->iter_next($i));
# eval using a for loop
$i=-1;
eval 'no strict; no warnings; for (@list) { ++$i; '.$p.' }';
my $e = $@;
$w{imPerl}->set(stock => $e ? 'gtk-no' : 'gtk-yes');
# compare and update the list
my $j=0; my $matched=0;
$i = $m->get_iter_first;
do {{
next if $m->get($i, C_FLG) & F_MAN;
my $match = $e || $m->get($i, C_OLD) eq $list[$j] ? 0 : 1;
$m->set($i, C_NEW, $list[$j++], C_FLG, $match);
$matched += $match;
}} while ($i = $m->iter_next($i));
# update status
$w{lbStatus}->set(label => $e
? 'Invalid expression'
: sprintf 'Matched %d/%d files.', $matched, scalar @list);
findconflicts();
}
sub findconflicts {
# create a list of old filenames
my @old;
my $i = $m->get_iter_first;
do {
push @old, $m->get($i, C_OLD);
} while ($i = $m->iter_next($i));
# search for modified items having a new filename in that list
my $e=0;
$i = $m->get_iter_first;
do {{
next if !$m->get($i, C_FLG);
my $new = $m->get($i, C_NEW);
my $match = $new eq '' || $new =~ /\// || grep $_ eq $new, @old;
$m->set($i, C_FLG, $m->get($i, C_FLG) | F_CON)
if $match;
$e++ if $match;
}} while ($i = $m->iter_next($i));
$w{lbStatus}->set(label => 'Errors found!') if $e;
}
sub dorename {
my $d = $w{fcDir}->get_current_folder;
# count number of renamable files
my $i = $m->get_iter_first;
my $count=0;
do {
$count++ if $m->get($i, C_FLG) && !($m->get($i, C_FLG) & F_CON);
} while ($i = $m->iter_next($i));
return $w{lbStatus}->set(label => 'Nothing to do...') if !$count;
$w{lbStatus}->set(label => 'Renaming...');
my $j=0;
$i = $m->get_iter_first;
do {{
next if !$m->get($i, C_FLG) || $m->get($i, C_FLG) & F_CON;
rename $d.'/'.$m->get($i, C_OLD), $d.'/'.$m->get($i, C_NEW);
$m->set($i, C_OLD, $m->get($i, C_NEW), C_FLG, 0);
$w{lbStatus}->set(label => sprintf 'Renaming file %d/%d...', ++$j, $count);
Gtk2->main_iteration_do(0);
}} while ($i = $m->iter_next($i));
$w{lbStatus}->set(label => sprintf 'Renamed %d files.', $j);
}

150
download/code/mdc2-parse.pl Normal file
View file

@ -0,0 +1,150 @@
#!/usr/bin/perl
# mdc3-parse.pl
# June 2007
# Yoran Heling
# License: MIT
use strict;
use warnings;
use Storable 'store', 'retrieve';
use POSIX;
use POSIX::strptime;
my $log = 'log'; # the microdc2 log file
my $result = 'transferred.html'; # the resulting html file
my $cache = 'logparse.pl_cache'; # file for the Storable cache
my %ts = -s $cache ? ( %{retrieve($cache)} ) : ();
my $totalsent = 0;
# using STDIN/OUT -> lazy and ugly :-)
open(STDIN, '<', $log) || die $!;
open(STDOUT, '>', $result) || die $!;
while(<>) {
s/\r?\n//;
# 28.04.2007 20:15:25
next if !s/^([0-9]{2}\.[0-9]{2}\.[0-9]{4}\s+[0-9]{2}:[0-9]{2}:[0-9]{2})\s//;
my $date = $1;
# plesnivec: Upload of `Ghost in the Shell - Stand Alone Complex - 25.avi' succeeded (transfer complete). 1.5MiB transferred in 53s (29KiB/s).
# [Anime]Spank: Upload of `Rozen Maiden - Traumend - 07.avi' succeeded (transfer complete). 177MiB transferred in 2h46m35s (19KiB/s).
# [izms]Inkognito: Upload of `Ultra Maniac TV - 16.avi' failed (communication error). 1.7MiB transferred in 3m4s (9KiB/s).
if(/^(.+): Upload of `(.+)' (succeeded|failed) \(([^)]+)\)\. ([0-9\.]+[GMK]?i?B) transferred in ([0-9dhms]+) \(([0-9]+)KiB\/s\)/) {
my($nick, $file, $sf, $msg, $sent, $time, $speed) = ($1, $2, $3, $4, $5, $6);
$file =~ s/\\'/'/g;
my $epoch = POSIX::mktime(POSIX::strptime($date, '%d.%m.%Y %T'));
$sent = calcsize($sent);
$time = calctime($time);
$totalsent += $sent;
my $k = $nick.$file;
next if $ts{$k} && grep { +$_ == $epoch } @{$ts{$k}{epoch}};
if($ts{$k}) {
$ts{$k}{sent} += $sent;
$ts{$k}{time} += $time;
$ts{$k}{chunks}++;
$ts{$k}{failed}++ if $sf eq 'failed';
$ts{$k}{succeeded}++ if $sf eq 'succeeded';
push(@{$ts{$k}{epoch}}, $epoch);
} else {
$ts{$k} = {
file => $file,
nick => $nick,
sent => $sent,
time => $time,
date => $date,
epoch => [ $epoch ], # only used for identification and sorting
# msg => $msg, # unused
chunks => 1,
failed => $sf eq 'failed' ? 1 : 0,
succeeded => $sf eq 'succeeded' ? 1 : 0,
};
}
}
}
store \%ts, $cache;
printf "\nSent %d files and %s\n", scalar keys %ts, calcsize($totalsent);
print qq|
<html>
<head>
<style>
table { font-size: 13px; }
td { padding: 0 5px 0 5px; border: 1px solid #ccc; white-space: nowrap; }
thead td { font-weight: bold; }
.tc4, .tc5, .tc7 { text-align: right }
</style>
</head>
<body>
<table cellspacing=0 cellpadding=0>
<thead><tr>
<td class="tc1">Date/time</td>
<td class="tc4">Transfered</td>
<td class="tc5">Time</td>
<td class="tc7">Speed</td>
<td class="tc6">Chunks</td>
<td class="tc2">User</td>
<td class="tc3">Filename</td>
</tr></thead>\n|;
printf(qq|<tr>
<td class="tc1">%s</td>
<td class="tc4">%s</td>
<td class="tc5">%s</td>
<td class="tc7">%.2f KiB/s</td>
<td class="tc6">%d/%d/%d</td>
<td class="tc2">%s</td>
<td class="tc3">%s</td>
</tr>\n|,
$ts{$_}{date},
calcsize($ts{$_}{sent}),
calctime($ts{$_}{time}),
$ts{$_}{sent}/($ts{$_}{time}||1),
$ts{$_}{chunks}, $ts{$_}{succeeded}, $ts{$_}{failed},
$ts{$_}{nick},
$ts{$_}{file},
)
for (sort { $ts{$b}{epoch}[0] <=> $ts{$a}{epoch}[0] } keys %ts);
print qq|</table>
</body>
</html>|;
sub calcsize { # in KBytes by default, to avoid a 32bit int overflow
if($_[0] =~ /^([0-9\.]+)(GiB|MiB|KiB|B)$/) {
return $2 eq 'B' ? $1/1024 :
$2 eq 'KiB' ? $1 :
$2 eq 'MiB' ? $1*1024 :
$1*1024*1024;
} else {
my $r = $_[0]; my $x = 'KiB';
if($r > 1024) { $r/=1024; $x='MiB' }
if($r > 1024) { $r/=1024; $x='GiB' }
return sprintf "%.1f %s", $r, $x;
}
}
sub calctime {
if($_[0] =~ /[dhms]/) {
my @t = reverse split(/[dhms]/, $_[0]);
return ($t[3]||0)*86400 + ($t[2]||0)*3600 + ($t[1]||0)*60 + $t[0];
} else {
my $r = $_[0]; my $x = '';
if($r > 24*3600) { $x .= int($r/(24*3600)).' d '; $r%=24*3600; }
$x .= sprintf '%d:%02d:%02d',
int($r/3600),
int(($r%3600)/60),
($r%3600) % 60;
return $x;
}
}

99
download/code/nccolour.c Normal file
View file

@ -0,0 +1,99 @@
/* nccolour.c - Print out some ncurses colours.
*
* Compile using:
* gcc nccolour.c -o nccolour -lncurses
*
* Written by Yoran Heling <projects@yorhel.nl>
*
* Date: 2011-06-11
* License: MIT
* Web: http://dev.yorhel.nl/dump/nccolour
*/
#include <stdlib.h>
#include <curses.h>
struct colour {
int code; char *desc;
} colours[] = {
{ -1, "default" },
{ COLOR_BLACK, "BLACK" },
{ COLOR_RED, "RED" },
{ COLOR_GREEN, "GREEN" },
{ COLOR_YELLOW, "YELLOW" },
{ COLOR_BLUE, "BLUE" },
{ COLOR_MAGENTA, "MAGENTA" },
{ COLOR_CYAN, "CYAN" },
{ COLOR_WHITE, "WHITE" },
};
void print_colourpart(int attr, int i) {
addstr(" ");
attron(attr | COLOR_PAIR(i*6+1));
addstr("FD");
addstr(" ");
attron(attr | COLOR_PAIR(i*6+2));
addstr("FB");
attroff(attr | COLOR_PAIR(i*6+2));
addstr(" ");
attron(attr | COLOR_PAIR(i*6+3));
addstr("FW");
attroff(attr | COLOR_PAIR(i*6+3));
addstr(" ");
attron(attr | COLOR_PAIR(i*6+4));
addstr("BG");
attroff(attr | COLOR_PAIR(i*6+4));
addstr(" ");
attron(attr | COLOR_PAIR(i*6+5));
addstr("BD");
attroff(attr | COLOR_PAIR(i*6+5));
}
int main(int argc, char **argv) {
if(!initscr()) {
printf("Error initializing screen.\n");
exit(1);
}
if(!has_colors()) {
printf("This terminal does not support colours.\n");
exit(1);
}
start_color();
// this prevents ncurses from forcing a white-on-black colour scheme,
// regardless of what scheme is used by the terminal.
use_default_colors();
attron(A_BOLD);
mvaddstr(0, 0, "Colour FD FB FW BG BD BFD BFB BFW BBG BBN");
attroff(A_BOLD);
int i;
for(i=0; i<sizeof(colours)/sizeof(struct colour); i++) {
init_pair(i*6+1, colours[i].code, -1);
init_pair(i*6+2, colours[i].code, COLOR_BLACK);
init_pair(i*6+3, colours[i].code, COLOR_WHITE);
init_pair(i*6+4, colours[i].code, colours[i].code);
init_pair(i*6+5, -1, colours[i].code);
mvaddstr(i+2, 1, colours[i].desc);
move(i+2, 8);
print_colourpart(0, i);
print_colourpart(A_BOLD, i);
}
mvaddstr(i+3, 2, "FD = Front colour on default background");
mvaddstr(i+4, 2, "FB = Front colour on black background");
mvaddstr(i+5, 2, "FW = Front colour on white background");
mvaddstr(i+6, 2, "BG = Front and background colour");
mvaddstr(i+7, 2, "BD = Background colour with default front colour");
mvaddstr(i+8, 2, "B?? = As above, with A_BOLD enabled");
mvaddstr(i+10, 0, "Hit any key to exit.");
refresh();
getch();
endwin();
return 0;
}

326
download/code/vinfo.c Normal file
View file

@ -0,0 +1,326 @@
/* vinfo - A small commandline visual novel info display thingy
Last modified: 2009-11-16
This is just a simple example program using the public VNDB API,
this program is neither useful nor actively maintained. Feel free
to extend it to suit your needs.
Requirements:
- A POSIX-compatible system
- GNU getopt()
- json-c (http://oss.metaparadigm.com/json-c/)
Compile:
gcc vinfo.c `pkg-config --cflags --libs json` -o vinfo
Usage:
vinfo -u <username> -p <password> <vnid>
TODO:
- Giving password on the commandline is a really bad idea,
need to fix an alternative method.
- Display more info on VN info output (links/description)
- Make host/port configurable
- VN searching
- More error checking on the received JSON data
- Producers/releases?
Copyright (c) 2009 Yoran Heling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define PROGRAM "vinfo"
#define VERSION 0.1
#define SERVER "81.204.242.156" /* beta, you may want to change this to whatever api.vndb.org resolves to */
#define PORT 19534
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <json.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#define WS "\011\012\015\040"
#define DIE(str) { perror(str); exit(1); }
char *user, *pass;
int sock;
/* sends command and returns reply */
char *docmd(char *cmd) {
char *res, *fmt;
unsigned int start, end, read = 0, size = 1024;
int r;
json_object *json;
if(send(sock, cmd, strlen(cmd), 0) < 0)
DIE("send");
/* receive data */
res = malloc(size);
while(memchr(res, 4, read) == NULL) {
if(read > 102400) {
printf("ERROR: Too long reply.\n");
exit(1);
}
if(size-read < 500) {
size *= 2;
res = realloc(res, size);
}
if((r = recv(sock, res+read, size-read, 0)) < 0)
DIE("recv");
read += r;
}
/* remove leading and trailing whitespace */
end = ((char *)memchr(res, 4, read))-res;
res[end--] = 0;
while(strspn(res+end, WS))
end--;
start = strspn(res, WS);
if(start == 0)
fmt = res;
else {
fmt = malloc(end-start+1);
strcpy(fmt, res+start);
free(res);
}
/* check for error response */
if(!strncmp(fmt, "error", strlen("error"))) {
res = fmt+strlen("error");
res += strspn(res, WS);
json = json_tokener_parse(res);
if(json != NULL && json_object_is_type(json, json_type_object)) {
printf("ERROR(%s): %s\n",
json_object_get_string(json_object_object_get(json, "id")),
json_object_get_string(json_object_object_get(json, "msg")));
json_object_put(json);
} else {
printf("Received something strange...\n> %s\n", fmt);
}
free(fmt);
exit(1);
}
return fmt;
}
/* opens the TCP socket and logs in */
void login() {
struct sockaddr_in addr;
json_object *arg;
const char *json;
char *cmd, *res;
/* connect */
if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
DIE("socket");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
inet_aton(SERVER, &addr.sin_addr);
if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
DIE("connect");
/* construct login command */
arg = json_object_new_object();
json_object_object_add(arg, "protocol", json_object_new_int(1));
json_object_object_add(arg, "client", json_object_new_string(PROGRAM));
json_object_object_add(arg, "clientver", json_object_new_double(VERSION));
json_object_object_add(arg, "username", json_object_new_string(user));
json_object_object_add(arg, "password", json_object_new_string(pass));
json = json_object_to_json_string(arg);
cmd = malloc(strlen(json) + 10);
sprintf(cmd, "login %s\04", json);
json_object_put(arg);
/* login */
res = docmd(cmd);
free(cmd);
if(strncmp("ok", res, 2)) {
printf("ERROR: Received something strange.\n> %s\n", res);
free(res);
exit(1);
}
free(res);
}
void vninfo(int vid) {
char cmd[50], *res, *tmp, *tmp2;
const char *str;
int i, n;
json_object *json, *vn, *itm;
login();
sprintf(cmd, "get vn basic,details (id=%d)\04", vid);
res = docmd(cmd);
if(strncmp("results", res, 7)) {
printf("ERROR: Received something strange.\n> %s\n", res);
exit(1);
}
tmp = res+7;
tmp += strspn(tmp, WS);
if((json = json_tokener_parse(tmp)) == NULL || !json_object_is_type(json, json_type_object)) {
printf("ERROR: Received something strange.\n> %s\n", res);
exit(1);
}
/*printf("\n%s\n\n", tmp);*/
/* note: may want to put in some checks on whether the data indeed confirms to what we expect */
if(json_object_get_int(json_object_object_get(json, "num")) < 1) {
printf("No VN with that ID.\n");
exit(1);
}
vn = json_object_array_get_idx(json_object_object_get(json, "items"), 0);
/* header + titles */
printf("------------------------------ v%-5d ----------------------------\n", vid);
printf(" Title : %s\n", json_object_get_string(json_object_object_get(vn, "title")));
if((str = json_object_get_string(json_object_object_get(vn, "original"))) != NULL)
printf(" Official : %s\n", str);
/* aliases (string processing in C, meh) */
str = json_object_get_string(json_object_object_get(vn, "aliases"));
if(str != NULL) {
printf(" Aliases : ");
tmp = strdup(str);
tmp2 = tmp;
i = -1;
do {
i++;
if(!tmp[i] || tmp[i] == ',' || tmp[i] == '\n') {
if(tmp2 != tmp)
printf(" ");
tmp2 += strspn(tmp2, ", \n");
n = tmp[i];
tmp[i] = 0;
printf("%s\n", tmp2);
if(n) {
i++;
tmp2 = tmp+i;
}
}
} while(tmp[i]);
free(tmp);
}
/* release date */
str = json_object_get_string(json_object_object_get(vn, "released"));
printf(" First release : %-10s\n", str != NULL ? str : "-");
/* length */
itm = json_object_object_get(vn, "length");
if(json_object_is_type(itm, json_type_int)) {
printf(" Length : ");
switch(json_object_get_int(itm)) {
case 1: printf("Very short\n"); break;
case 2: printf("Short\n"); break;
case 3: printf("Medium\n"); break;
case 4: printf("Long\n"); break;
case 5: printf("Very long\n"); break;
}
}
/* languages */
itm = json_object_object_get(vn, "languages");
if(json_object_array_length(itm) > 0) {
printf(" Available in : ");
for(i=0; i<json_object_array_length(itm); i++) {
if(i)
printf(", ");
printf("%s", json_object_get_string(json_object_array_get_idx(itm, i)));
}
printf("\n");
}
/* platforms */
itm = json_object_object_get(vn, "platforms");
if(json_object_array_length(itm) > 0) {
printf(" ...for : ");
for(i=0; i<json_object_array_length(itm); i++) {
if(i)
printf(", ");
printf("%s", json_object_get_string(json_object_array_get_idx(itm, i)));
}
printf("\n");
}
json_object_put(json);
free(res);
}
void usage() {
printf("%s -u username -p password query\n", PROGRAM);
exit(1);
}
int main(int argc, char **argv) {
char *query;
int r;
user = pass = query = NULL;
while((r = getopt(argc, argv, "-u:p:")) >= 0) {
switch(r) {
case 'u':
user = optarg;
break;
case 'p':
pass = optarg;
break;
case '\1':
query = optarg;
break;
default:
usage();
}
}
if(!pass || !user || !query)
usage();
if(sscanf(query, "%d", &r) == 1 && r > 0) {
vninfo(r);
} else {
printf("Sorry, searching is not supported yet\n");
exit(1);
}
return 0;
}

342
download/code/yapong-0.01.c Normal file
View file

@ -0,0 +1,342 @@
/*
* YAPong.c 0.01 - Yet Another pong clone, made by //YorHel
*
* Copyright 2006 Y.Heling.
* 2006-02-22
* License: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <signal.h>
#include <sys/time.h>
#define DI_UP 1
#define DI_DOWN 2
#define DI_RIGHT 3
#define DI_LEFT 4
void movepadel(char, char);
void startgame();
void drawpadels();
void updatescore();
void drawframe();
void updatedot();
void movedot(int);
int print_help(char *);
int winrows, wincols, padsize, padposL, padposR, scoreL,
scoreR, dotX, dotY, delay, padstep, dotstep;
char direX, direY, started, paused, scored, autosize;
WINDOW *win;
int main(int argc, char *argv[]) {
/* defaults */
autosize = 0;
padstep = 1;
dotstep = 1;
winrows = 25; wincols = 80;
padsize = ((winrows-3)/10);
delay = 50 * 1000;
/* args */
{
char *name = *argv++;
char *ch;
while(ch = *argv++) {
if(!strcmp(ch, "-h"))
return print_help(name);
else if(!strcmp(ch, "-l")) {
int level = atoi(*argv++);
switch(level) {
case 1 : delay = 100*1000; padsize = 6; padstep = 2; dotstep = 1; break;
case 2 : delay = 80*1000; padsize = 6; padstep = 2; dotstep = 1; break;
case 3 : delay = 65*1000; padsize = 5; padstep = 1; dotstep = 1; break;
case 4 : delay = 50*1000; padsize = 3; padstep = 1; dotstep = 1; break;
case 5 : delay = 80*1000; padsize = 3; padstep = 1; dotstep = 2; break;
default :
printf("ERROR: unknown level %d\n", level);
return 0; break;
}
}
else if(!strcmp(ch, "-d")) {
delay = atoi(*argv++) * 1000;
if(delay >= 1000*1000 || delay == 0) {
printf("ERROR: delay should be an integer between 0 and 1000\n");
return 1;
}
}
else if(!strcmp(ch, "-p")) {
padsize = atoi(*argv++);
if(padsize > wincols-3 || padsize == 0) {
printf("ERROR: padel size should at least be 1 and should not be too high\n");
return 1;
}
}
else if(!strcmp(ch, "-s")) {
if(!strcmp(*argv, "auto")) {
autosize = 1; *argv++;
} else {
wincols = atoi(*argv++);
winrows = atoi(*argv++);
autosize = 0;
if(wincols < 20 || winrows < 5) {
printf("ERROR: Window size should at least be 20x5\n");
return 1;
}
}
}
else if(!strcmp(ch, "-ps")) {
padstep = atoi(*argv++);
if(padstep > padsize || padstep <= 0) {
printf("ERROR: padstep should at least be 0 and should not be larger than padsize\n");
return 1;
}
}
else if(!strcmp(ch, "-ds")) {
dotstep = atoi(*argv++);
if(dotstep <= 0) {
printf("ERROR: dotstep should be a positive integer\n");
return 1;
}
}
else {
printf("ERROR: no such option: %s\n", ch);
return print_help(name);
}
}
}
/* initialize */
if( (win = initscr()) == NULL) {
fprintf(stderr, "ERROR: Can't initialize ncurses\n");
return(1);
}
cbreak(); /* imidiatly get input chars */
noecho(); /* don't print input chars */
keypad(stdscr, TRUE); /* also get other keys */
curs_set(0); /* hide cursor */
if(autosize)
getmaxyx(stdscr, winrows, wincols);
/* non-changable stuff */
padposL = padposR = ((winrows/2)-(padsize/2));
scoreL = scoreR = 0;
dotX = (wincols/2);
dotY = (winrows/2)-1;
direX = DI_RIGHT;
direY = DI_DOWN;
started = paused = 0;
mvaddstr(0, 0, "Initializing, please wait...");
refresh();
clear();
/* draw stuff */
drawframe();
drawpadels();
mvaddch(dotY, dotX, '0');
refresh();
/* get keyboard input */
int ch;
while(1) {
ch = getch();
switch(ch) {
case KEY_UP :
movepadel('r', 'u'); break;
case KEY_DOWN :
movepadel('r', 'd'); break;
case 'a' :
movepadel('l', 'u'); break;
case 'z' :
movepadel('l', 'd'); break;
case ' ' :
if(!started)
startgame();
else if(scored) {
scored = 0;
mvaddch(dotY, dotX, ' ');
dotX = (wincols/2);
dotY = (winrows/2)-1;
refresh();
} else if(!paused)
paused = 1;
else if(paused)
paused = 0;
break;
case 'q' :
goto end;
break;
}
}
/* cleanup */
end:
erase();
mvaddstr(0, 0, "Thank you for playing Yet Another pong game. :)\nPlease visit http://yorhel.nl/ when your bored...\n");
refresh();
delwin(win);
endwin();
refresh();
return 0;
}
void movepadel(char which, char direct) {
if(paused || scored || !started)
return;
int *pos = which == 'r' ? &padposR : &padposL;
*pos += direct == 'u' ? -1*padstep : padstep;
if(*pos < 2)
*pos = 2;
if(*pos > winrows-padsize-2)
*pos = winrows-padsize-2;
drawpadels();
move(wincols-1, 0);
refresh();
}
void startgame() {
struct itimerval t;
t.it_interval.tv_sec = t.it_value.tv_sec = 0;
t.it_interval.tv_usec = t.it_value.tv_usec = delay;
setitimer(ITIMER_REAL, &t, NULL);
struct sigaction s;
s.sa_handler = movedot;
s.sa_flags = 0;
sigemptyset(&s.sa_mask);
sigaction(SIGALRM, &s, NULL);
started = 1;
}
void drawframe() {
mvaddstr(0, 3, "left");
mvaddstr(0, wincols-8, "right");
mvaddstr(0, ((wincols/2)-7), "YAPong by //YorHel");
move(1, 0);
hline(ACS_HLINE, wincols);
move(winrows-1, 0);
hline(ACS_HLINE, wincols);
updatescore();
}
void updatescore() {
char scl[3], scr[3];
sprintf(scl, "%02d", scoreL);
sprintf(scr, "%02d", scoreR);
mvaddstr(0, 0, scl);
mvaddstr(0, wincols-2, scr);
}
void drawpadels() {
int c;
for(c=winrows; c--; ) {
if(c < 2 || c >= winrows-1)
continue;
/* left paddel */
if(c >= padposL && c <= padposL+padsize)
mvaddch(c, 0, ' ' | A_REVERSE);
else
mvaddch(c, 0, ' ');
/* right paddel */
if(c >= padposR && c <= padposR+padsize)
mvaddch(c, wincols-1, ' ' | A_REVERSE);
else
mvaddch(c, wincols-1, ' ');
}
}
/* called from signal */
void movedot(int signal) {
if(signal != SIGALRM || !started || paused || scored)
return;
mvaddch(dotY, dotX, ' ');
dotX += direX == DI_LEFT ? -1*dotstep : dotstep;
dotY += direY == DI_UP ? -1*dotstep : dotstep;
if(direY == DI_UP && dotY <= 2) {
direY = DI_DOWN;
dotY = 2;
}
if(direY == DI_DOWN && dotY >= winrows-2) {
direY = DI_UP;
dotY = winrows-2;
}
if(direX == DI_RIGHT && dotX >= wincols-2) {
direX = DI_LEFT;
dotX = wincols-2;
if(dotY < padposR || dotY > padposR + padsize) {
scored = 1;
scoreL++;
direX = DI_LEFT;
updatescore();
}
}
if(direX == DI_LEFT && dotX <= 1) {
direX = DI_RIGHT;
dotX = 1;
if(dotY < padposL || dotY > padposL + padsize) {
scored = 1;
scoreR++;
direX = DI_RIGHT;
updatescore();
}
}
mvaddch(dotY, dotX, '0');
move(wincols-1, 0);
refresh();
}
int print_help(char *name) {
printf("Usage: %s [options]\n\n", name);
printf("Options:\n");
printf(" -h This help message.\n");
printf(" -d int Specifies the interval between every move\n");
printf(" of the dot. You can speed up the game by\n");
printf(" setting this to a lower value.\n");
printf(" -p int Specifies the size of each padel in pixels\n");
printf(" -s x y Specifies the size of the window, or set\n");
printf(" to 'auto' to use the size of your current\n");
printf(" terminal. Recommended and default size is\n");
printf(" 80x25.\n");
printf(" -ps int Specifies the amount of pixels the padels\n");
printf(" move each time\n");
printf(" -ds int The amount of pixels the dot moves each\n");
printf(" time, setting this to a larger value\n");
printf(" increases game speed a lot.\n");
printf(" -l level The game-level, higher levels are more\n");
printf(" difficult. See below for more information\n\n");
printf("Levels:\n");
printf(" option equals to\n");
printf(" -l 1 -d 100 -p 6 -ps 2 -ds 1\n");
printf(" -l 2 -d 80 -p 6 -ps 2 -ds 1\n");
printf(" -l 3 -d 65 -p 5 -ps 1 -ds 1\n");
printf(" -l 4 -d 50 -p 3 -ps 1 -ds 1\n");
printf(" -l 5 -d 80 -p 3 -ps 1 -ds 2\n");
printf(" Please note that all the levels are meant for a 80x25\n");
printf(" window size, level 5 might be very easy with a higher\n");
printf(" window size. In that case you should change the\n");
printf(" the options yourself.\n\n");
printf("Ingame keys:\n");
printf(" space Start/pause/resume game\n");
printf(" key_up Move right padel up\n");
printf(" key_down Move right padel down\n");
printf(" a Move left padel up\n");
printf(" z Move left padel down\n");
printf(" q Quit game\n");
return 0;
}

335
download/code/yapong.c Normal file
View file

@ -0,0 +1,335 @@
/*
* YAPong.c 0.02 - Yet Another pong clone, made by //YorHel
*
* Copyright 2006 Y.Heling.
* 2006-02-23
* License: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#define DI_UP 1
#define DI_DOWN 2
#define DI_RIGHT 3
#define DI_LEFT 4
void movepadel(char, char);
void drawpadels();
void updatescore();
void drawframe();
void updatedot();
void movedot();
int print_help(char *);
int winrows, wincols, padsize, padposL, padposR, scoreL,
scoreR, dotX, dotY, delay, padstep, dotstep;
char direX, direY, started, paused, scored, autosize, autopsize;
WINDOW *win;
int main(int argc, char *argv[]) {
/* defaults */
autosize = 1;
autopsize = 1;
padstep = 1;
dotstep = 1;
winrows = 25; wincols = 80;
padsize = 0;
delay = 50;
/* args */
{
char *name = *argv++;
char *ch;
while(ch = *argv++) {
if(!strcmp(ch, "-h"))
return print_help(name);
else if(!strcmp(ch, "-l")) {
int level = atoi(*argv++);
switch(level) {
case 1 : delay = 100; padsize = 6; padstep = 2; dotstep = 1; break;
case 2 : delay = 80; padsize = 6; padstep = 2; dotstep = 1; break;
case 3 : delay = 65; padsize = 5; padstep = 1; dotstep = 1; break;
case 4 : delay = 50; padsize = 3; padstep = 1; dotstep = 1; break;
case 5 : delay = 80; padsize = 3; padstep = 1; dotstep = 2; break;
default :
printf("ERROR: unknown level %d\n", level);
return 0; break;
}
}
else if(!strcmp(ch, "-d")) {
delay = atoi(*argv++);
if(delay >= 1000 || delay == 0) {
printf("ERROR: delay should be an integer between 0 and 1000\n");
return 1;
}
}
else if(!strcmp(ch, "-p")) {
if(!strcmp(*argv, "auto")) {
autopsize = 1; *argv++;
} else {
autopsize = 0;
padsize = atoi(*argv++);
if(padsize > wincols-3 || padsize == 0) {
printf("ERROR: padel size should at least be 1 and should not be too high\n");
return 1;
}
}
}
else if(!strcmp(ch, "-s")) {
if(!strcmp(*argv, "auto")) {
autosize = 1; *argv++;
} else {
wincols = atoi(*argv++);
winrows = atoi(*argv++);
autosize = 0;
if(wincols < 20 || winrows < 5) {
printf("ERROR: Window size should at least be 20x5\n");
return 1;
}
}
}
else if(!strcmp(ch, "-ps")) {
padstep = atoi(*argv++);
if(padstep <= 0) {
printf("ERROR: padstep should at least be 1\n");
return 1;
}
}
else if(!strcmp(ch, "-ds")) {
dotstep = atoi(*argv++);
if(dotstep <= 0) {
printf("ERROR: dotstep should be a positive integer\n");
return 1;
}
}
else {
printf("ERROR: no such option: %s\n", ch);
return print_help(name);
}
}
}
/* initialize */
if( (win = initscr()) == NULL) {
fprintf(stderr, "ERROR: Can't initialize ncurses\n");
return(1);
}
cbreak(); /* imidiatly get input chars */
noecho(); /* don't print input chars */
keypad(stdscr, TRUE); /* also get other keys */
curs_set(0); /* hide cursor */
nodelay(stdscr, TRUE);
if(autosize)
getmaxyx(stdscr, winrows, wincols);
if(autopsize)
padsize = ((winrows-3)/10)*2;
/* non-changable stuff */
padposL = padposR = ((winrows/2)-(padsize/2));
scoreL = scoreR = 0;
dotX = (wincols/2);
dotY = (winrows/2)-1;
direX = DI_RIGHT;
direY = DI_DOWN;
started = paused = 0;
/* draw stuff */
drawframe();
drawpadels();
mvaddch(dotY, dotX, '0');
refresh();
/* main loop */
delay = delay/15; /* assume napms() takes 50% more time */
int ch; int d = 0;
while(1) {
ch = getch();
switch(ch) {
case ERR : break;
case KEY_UP :
movepadel('r', 'u'); break;
case KEY_DOWN :
movepadel('r', 'd'); break;
case 'a' :
movepadel('l', 'u'); break;
case 'z' :
movepadel('l', 'd'); break;
case ' ' :
if(!started)
started = 1;
else if(scored) {
scored = 0;
mvaddch(dotY, dotX, ' ');
dotX = (wincols/2);
dotY = (winrows/2)-1;
refresh();
} else if(!paused)
paused = 1;
else if(paused)
paused = 0;
break;
case 'q' :
goto end;
break;
}
if(++d == delay) {
d = 0;
movedot();
}
napms(10);
}
/* cleanup */
end:
erase();
mvaddstr(0, 0, "Thank you for playing Yet Another pong game. :)\nPlease visit http://yorhel.nl/ when your even more bored...\n");
refresh();
delwin(win);
endwin();
refresh();
return 0;
}
void movepadel(char which, char direct) {
if(paused || scored || !started)
return;
int *pos = which == 'r' ? &padposR : &padposL;
*pos += direct == 'u' ? -1*padstep : padstep;
if(*pos < 2)
*pos = 2;
if(*pos > winrows-padsize-2)
*pos = winrows-padsize-2;
drawpadels();
move(wincols-1, 0);
refresh();
}
void drawframe() {
mvaddstr(0, 3, "left");
mvaddstr(0, wincols-8, "right");
mvaddstr(0, ((wincols/2)-7), "YAPong by //YorHel");
move(1, 0);
hline(ACS_HLINE, wincols);
move(winrows-1, 0);
hline(ACS_HLINE, wincols);
updatescore();
}
void updatescore() {
char scl[3], scr[3];
sprintf(scl, "%02d", scoreL);
sprintf(scr, "%02d", scoreR);
mvaddstr(0, 0, scl);
mvaddstr(0, wincols-2, scr);
}
void drawpadels() {
int c;
for(c=winrows; c--; ) {
if(c < 2 || c >= winrows-1)
continue;
/* left paddel */
if(c >= padposL && c <= padposL+padsize)
mvaddch(c, 0, ' ' | A_REVERSE);
else
mvaddch(c, 0, ' ');
/* right paddel */
if(c >= padposR && c <= padposR+padsize)
mvaddch(c, wincols-1, ' ' | A_REVERSE);
else
mvaddch(c, wincols-1, ' ');
}
}
void movedot() {
if(!started || paused || scored)
return;
mvaddch(dotY, dotX, ' ');
dotX += direX == DI_LEFT ? -1*dotstep : dotstep;
dotY += direY == DI_UP ? -1*dotstep : dotstep;
if(direY == DI_UP && dotY <= 2) {
direY = DI_DOWN;
dotY = 2;
}
if(direY == DI_DOWN && dotY >= winrows-2) {
direY = DI_UP;
dotY = winrows-2;
}
if(direX == DI_RIGHT && dotX >= wincols-2) {
direX = DI_LEFT;
dotX = wincols-2;
if(dotY < padposR || dotY > padposR + padsize) {
scored = 1;
scoreL++;
direX = DI_LEFT;
updatescore();
}
}
if(direX == DI_LEFT && dotX <= 1) {
direX = DI_RIGHT;
dotX = 1;
if(dotY < padposL || dotY > padposL + padsize) {
scored = 1;
scoreR++;
direX = DI_RIGHT;
updatescore();
}
}
mvaddch(dotY, dotX, '0');
move(wincols-1, 0);
refresh();
}
int print_help(char *name) {
printf("Usage: %s [options]\n\n", name);
printf("Options:\n");
printf(" -h This help message.\n");
printf(" -d int Specifies the interval between every move\n");
printf(" of the dot. You can speed up the game by\n");
printf(" setting this to a lower value.\n");
printf(" -p int Specifies the size of each padel in pixels,\n");
printf(" set to auto to automatically calculate the\n");
printf(" size using the window size.\n");
printf(" -s x y Specifies the size of the window, or set\n");
printf(" to 'auto' to use the size of your current\n");
printf(" terminal. Recommended size: 80x25, default:\n");
printf(" auto.\n");
printf(" -ps int Specifies the amount of pixels the padels\n");
printf(" move each time\n");
printf(" -ds int The amount of pixels the dot moves each\n");
printf(" time, setting this to a larger value\n");
printf(" increases game speed a lot.\n");
printf(" -l level The game-level, higher levels are more\n");
printf(" difficult. See below for more information\n\n");
printf("Levels:\n");
printf(" option equals to\n");
printf(" -l 1 -d 100 -p 6 -ps 2 -ds 1\n");
printf(" -l 2 -d 80 -p 6 -ps 2 -ds 1\n");
printf(" -l 3 -d 65 -p 5 -ps 1 -ds 1\n");
printf(" -l 4 -d 50 -p 3 -ps 1 -ds 1\n");
printf(" -l 5 -d 80 -p 3 -ps 1 -ds 2\n");
printf(" Please note that all the levels are meant for a 80x25\n");
printf(" window size, level 5 might be very easy with a higher\n");
printf(" window size. In that case you should change the\n");
printf(" the options yourself.\n\n");
printf("Ingame keys:\n");
printf(" space Start/pause/resume game\n");
printf(" key_up Move right padel up\n");
printf(" key_down Move right padel down\n");
printf(" a Move left padel up\n");
printf(" z Move left padel down\n");
printf(" q Quit game\n");
return 0;
}

View file

@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEdEYNMrgIEOupr6LpYjlMaYwnOfoFAlpu/qsACgkQYjlMaYwn
OfqxAA//QtGz3x1IdBJo1VFblt9MNiaI5C/YOP7u40u72147+DX7wqsXBG/LgSBM
3CY9+GPg4gRZTN/ylaAEF7lqb3epsP76BBiIkqErs70DkR27svdMCmv5I7II8X38
w2HmT0A5dD0ktn5wih4YOepjhMGzw9LQG5/yQjrTHzWoZHVkYhXB5zamNdMmnvPC
yI4/zcwMxLrFYvc0aUNDDauwaCHbDmyuxcbKRm+SShSE53QLz/whl4wrBDGE1Oxo
7JAIgwWyu6D2NRYET4x3oipeFmeaBK8IbJLA/oO8mY76iutu5Nu9+xo3xYftJhKw
WB4HxxNvfMqX1d2kO1TJAiDYu4eCl6ND3nZZyPjxH07vLYC3g8ddGsCvVEgDKyrs
JD6NFMvDVEiL/RG01gnvwkVi704maT0LHC4l6GsAXb4mpNHwKT0HNHHCAFnBDwAf
hqMHhBZYIvj90RrSP9vRx/rrhg55s+MPddb/o149z+L/hapF7IHpJnrNsBf5MGrW
2Q64d7mc2sYsm03KcXI5bfVATMALRfbJK9XQHFjIxggdVd6j+F+vOsQz0T93Wwx7
uHIqtB0NQD4CxHl5119m7KU0BFV0Qpe1rtI4ZGlGmGAhpUVx2cGpdH8GA1p1FA/2
dAqQQbOjEpMsFZ5PZ2uE8aOSz+lMqSLPzEuV+VJK/Y3sLNFzmrs=
=ze3z
-----END PGP SIGNATURE-----

View file

@ -0,0 +1 @@
67239592ac41f42290f52ab89ff198be ncdu-1.13.tar.gz

View file

@ -0,0 +1 @@
3233c4185208d9989ac528a94817ed92dd59c773 ncdu-1.13.tar.gz

BIN
img/ncduconfirm-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
img/ncdudone-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
img/ncduhelp1-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
img/ncduhelp2-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
img/ncduinfo-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
img/ncduscan-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -3,7 +3,7 @@
use strict;
use warnings;
use utf8;
use TUWF ':html', ':xml';
use TUWF ':html';
use POSIX 'strftime';
use Cwd 'abs_path';
@ -12,6 +12,10 @@ BEGIN { ($ROOT = abs_path $0) =~ s{index\.cgi$}{}; }
my @changes = (
[ '2018-01-29', '/ncdu', 'ncdu 1.13 released' ],
[ '2018-01-24', '/nginx-confgen', 'nginx-confgen 1.1 released' ],
[ '2018-01-19', '/nginx-confgen', 'New project: nginx-confgen' ],
[ '2017-05-28', '/doc/funcweb', 'New article: An Opinionated Survey of Functional Web Development' ],
[ '2016-12-30', '/ncdc', 'ncdc 1.20 released' ],
[ '2016-08-24', '/ncdu', 'ncdu 1.12 released' ],
[ '2016-08-16', '/dump/btrfssize', 'Uploaded btrfs-size.pl' ],
@ -109,6 +113,9 @@ TUWF::register(
qr{globster/daemon} => sub { podpage(shift, 'globster-daemon', 'globster', 'daemon', 'The globster(1) Man Page', 1) },
qr{globster/launch} => sub { podpage(shift, 'globster-launch', 'globster', 'launch', 'The globster-launch(1) Man Page', 1) },
qr{globster/ctl} => sub { podpage(shift, 'globster-ctl', 'globster', 'ctl', 'The globsterctl(1) Man Page', 1) },
qr{nginx-confgen} => sub { podpage(shift, 'nginx-confgen', 'nginx-confgen', '', 'Nginx Configuration Generator') },
qr{nginx-confgen/man} => sub { podpage(shift, 'nginx-confgen-man', 'nginx-confgen', 'man', 'The nginx-confgen(1) Man Page', 1) },
qr{nginx-confgen/changes} => sub { changelog(shift, 'nginx-confgen-changelog', undef, 'nginx-confgen', 'changes', 'nginx-confgen Changelog', 1) },
qr{tuwf} => sub { podpage(shift, 'tuwf', 'tuwf', '', 'The Ultimate Website Framework') },
qr{tuwf/man(?:/(db|misc|request|response|xml))?} => \&tuwfmanual,
qr{tuwf/changes} => sub { changelog(shift, 'tuwf-changelog', 'TUWF', 'tuwf', 'changes', 'TUWF Changelog') },
@ -120,6 +127,7 @@ TUWF::register(
qr{doc/commvis} => sub { podpage(shift, 'doc-commvis', 'doc', '', 'A Distributed Communication System for Modular Applications', 1) },
qr{doc/dcstats} => sub { podpage(shift, 'doc-dcstats', 'doc', '', 'Some Measurements on Direct Connect File Lists', 1) },
qr{doc/easyipc} => sub { podpage(shift, 'doc-easyipc', 'doc', '', 'The Sorry State of Convenient IPC', 1) },
qr{doc/funcweb} => sub { podpage(shift, 'doc-funcweb', 'doc', '', 'An Opinionated Survey of Functional Web Development') },
qr{dump} => sub { podpage(shift, 'dump', 'dump', '', 'Code dump') },
qr{demo} => sub { podpage(shift, 'dump-demo', 'dump', 'demo', 'Demos') },
qr{dump/awshrink} => sub { podpage(shift, 'dump-awshrink', 'dump', 'awshrink', 'AWStats Data File Shrinker') },
@ -165,8 +173,11 @@ sub changelog {
$v =~ s/^([0-9]+\.[0-9]+(?:\.[0-9]+)?)\s+-\s+([0-9]{4}-[0-9]{2}-[0-9]{2})//;
li style => 'list-style-type: none; margin: 0';
b $1;
txt " - $2 - ";
lit $s->genDLLink("$pr-$1.tar.gz");
txt " - $2";
if($pr) {
txt ' - ';
lit $s->genDLLink("$pr-$1.tar.gz");
}
br;
ul;
for (split(/\r?\n\s+-\s+/, $v)) {
@ -203,7 +214,7 @@ sub atom {
my $t = (stat("$ROOT/index.cgi"))[9];
$s->resHeader('Last-Modified' => strftime '%a, %d %b %Y %H:%M:%S GMT', gmtime $t);
$s->resHeader('Content-Type' => 'application/atom+xml');
xml;
TUWF::XML::xml;
tag feed => xmlns => 'http://www.w3.org/2005/Atom', 'xml:lang' => 'en', 'xml:base' => 'https://dev.yorhel.nl/';
tag title => $sub ? "\u$sub Project Announcements" : "Yorhel's Projects";
tag updated => strftime('%Y-%m-%dT%H:%M:%SZ', gmtime $t);
@ -387,6 +398,7 @@ sub htmlPOD {
globster-launch(1) /globster/launch
globster(1) /globster/daemon
globster-api(7) /globster/api
ncdu(1) /ncdu/man
|;
return $l{$l} || ($l =~ /(.+)\((.)\)/ and "http://manned.org/$1.$2");
};
@ -444,7 +456,7 @@ sub genChanges {
sub htmlHeader {
my $s = shift;
my %o = (
spec => { map +($_,1), qw|ncdu ncdc globster tuwf yxml| },
spec => { map +($_,1), qw|ncdu ncdc globster tuwf yxml nginx-confgen| },
page => '',
sec => '',
sec2 => '',
@ -548,12 +560,17 @@ sub htmlMenu {
$m->('/yxml', 'Info', !$o{sec});
$m->('/yxml/man', 'Manual', $o{sec} eq 'man');
$m->('/yxml/bug', 'Bug tracker', $o{sec} eq 'bug');
} elsif($o{page} eq 'nginx-confgen') {
$m->('/nginx-confgen', 'Info', !$o{sec});
$m->('/nginx-confgen/man', 'Manual', $o{sec} eq 'man');
$m->('/nginx-confgen/changes', 'Changelog', $o{sec} eq 'changes');
} else {
$m->('/', 'Home', !$o{page});
$m->('/ncdu', 'Ncdu ');
$m->('/ncdc', 'Ncdc ');
$m->('/tuwf', 'Tuwf ');
$m->('/yxml', 'Yxml ');
$m->('/nginx-confgen', 'Nginx-cfg ');
$m->('/ylib', 'Ylib', $o{page} eq 'ylib');
$m->('/doc', 'Writing', $o{page} eq 'doc');
$m->('/dump', 'Code dump', $o{page} eq 'dump', sub {