Whole bunch of changes and releases
This commit is contained in:
parent
07143cfb36
commit
54947f15c2
36 changed files with 3269 additions and 40 deletions
4
dat/doc
4
dat/doc
|
|
@ -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
571
dat/doc-funcweb
Normal 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.
|
||||
2
dat/dump
2
dat/dump
|
|
@ -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
|
||||
|
||||
|
|
|
|||
9
dat/ncdc
9
dat/ncdc
|
|
@ -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:
|
||||
|
|
|
|||
10
dat/ncdu
10
dat/ncdu
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
33
dat/ncdu-man
33
dat/ncdu-man
|
|
@ -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
|
||||
|
|
|
|||
18
dat/ncdu-scr
18
dat/ncdu-scr
|
|
@ -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
54
dat/nginx-confgen
Normal 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;
|
||||
}
|
||||
|
||||
█
|
||||
}
|
||||
}
|
||||
|
||||
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/>.
|
||||
8
dat/nginx-confgen-changelog
Normal file
8
dat/nginx-confgen-changelog
Normal 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
285
dat/nginx-confgen-man
Normal 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;
|
||||
█
|
||||
}
|
||||
}
|
||||
# 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>
|
||||
12
dat/tuwf
12
dat/tuwf
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
16
download/TUWF-1.1.tar.gz.asc
Normal file
16
download/TUWF-1.1.tar.gz.asc
Normal 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-----
|
||||
1
download/TUWF-1.1.tar.gz.md5
Normal file
1
download/TUWF-1.1.tar.gz.md5
Normal file
|
|
@ -0,0 +1 @@
|
|||
4629606ce88c28e2ea767bf7b356fe9c TUWF-1.1.tar.gz
|
||||
1
download/TUWF-1.1.tar.gz.sha1
Normal file
1
download/TUWF-1.1.tar.gz.sha1
Normal file
|
|
@ -0,0 +1 @@
|
|||
7f2c4018674f7009db5d3d30218d379210c3a972 TUWF-1.1.tar.gz
|
||||
166
download/code/awshrink
Normal file
166
download/code/awshrink
Normal 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
330
download/code/bbcode.c
Normal 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, "&");
|
||||
else if(c == '<')
|
||||
fprintf(pi->dest, "<");
|
||||
else if(c == '>')
|
||||
fprintf(pi->dest, ">");
|
||||
else
|
||||
fprintf(pi->dest, "%c", c);
|
||||
}
|
||||
|
||||
void convertchararg(char *to, const int c) {
|
||||
switch(c) {
|
||||
case '\n' :
|
||||
sprintf(to, ""); break;
|
||||
case '&' :
|
||||
sprintf(to, "&"); break;
|
||||
case '"' :
|
||||
sprintf(to, """); 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
180
download/code/echoserv.c
Normal 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);
|
||||
}
|
||||
|
||||
248
download/code/grenamr-0.1.pl
Normal file
248
download/code/grenamr-0.1.pl
Normal 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
150
download/code/mdc2-parse.pl
Normal 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
99
download/code/nccolour.c
Normal 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
326
download/code/vinfo.c
Normal 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
342
download/code/yapong-0.01.c
Normal 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
335
download/code/yapong.c
Normal 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;
|
||||
}
|
||||
|
||||
16
download/ncdu-1.13.tar.gz.asc
Normal file
16
download/ncdu-1.13.tar.gz.asc
Normal 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-----
|
||||
1
download/ncdu-1.13.tar.gz.md5
Normal file
1
download/ncdu-1.13.tar.gz.md5
Normal file
|
|
@ -0,0 +1 @@
|
|||
67239592ac41f42290f52ab89ff198be ncdu-1.13.tar.gz
|
||||
1
download/ncdu-1.13.tar.gz.sha1
Normal file
1
download/ncdu-1.13.tar.gz.sha1
Normal file
|
|
@ -0,0 +1 @@
|
|||
3233c4185208d9989ac528a94817ed92dd59c773 ncdu-1.13.tar.gz
|
||||
BIN
img/ncduconfirm-2.png
Normal file
BIN
img/ncduconfirm-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
img/ncdudone-2.png
Normal file
BIN
img/ncdudone-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
img/ncduhelp1-2.png
Normal file
BIN
img/ncduhelp1-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
img/ncduhelp2-2.png
Normal file
BIN
img/ncduhelp2-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
img/ncduinfo-2.png
Normal file
BIN
img/ncduinfo-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
img/ncduscan-2.png
Normal file
BIN
img/ncduscan-2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
27
index.cgi
27
index.cgi
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue