diff --git a/dat/doc b/dat/doc index 2dfbc0d..ac48c15 100644 --- a/dat/doc +++ b/dat/doc @@ -8,6 +8,10 @@ rare occasions are published on this page. =over +=item C<2017-05-28 > - L + +The title says it all. + =item C<2014-07-29 > - L A long rant about IPC systems. diff --git a/dat/doc-funcweb b/dat/doc-funcweb new file mode 100644 index 0000000..cdc1d48 --- /dev/null +++ b/dat/doc-funcweb @@ -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<< >> + +When compared to native desktop application development, web development just +sucks. Native development is relatively simple with toolkits such as +L, L 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" any other way than through a +website. And the entire purpose of L is to +provide quick access to man pages from anywhere, which is not easily possible +with native applications. + +B<< >> + +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 (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 and browser support for L, 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 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 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. 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 +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. +It comes with an OCaml to JavaScript compiler, a web server, several handy +libraries, and a L to put everything +together. Its L 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 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, 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. (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, in +addition to the more theoretical L. + +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, which works alright most of the time. +Packaging is still often complex and messy, but it's certainly improving as +L 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 is a web framework inspired by +L. 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. + +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 is very similar to Scotty, but comes with +type-safe routing and various other goodies such as session and state +management, L +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 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, L and +L 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 +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, +L and +L. 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. 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, which are +run-time interpreted templates, like similar systems in most other languages. +Yesod comes with L, +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 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. 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 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 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 of libraries +available. + +=item Haste + +L 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 +(HTML DSL, Reactive UI), L +(another HTML DSL), L (yet +another HTML DSL) and +L (I 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, 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, +L and the more recent +L. 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 is the spiritual successor of Fay - +except it does not try to be compatible with Haskell, and in fact +L +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 package repository. Of note is +L, 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 +example already compiles down to a hefty 300 KB for me. + +=item Elm + +L 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 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 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 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. 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. 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. diff --git a/dat/dump b/dat/dump index 05b8605..85d00f5 100644 --- a/dat/dump +++ b/dat/dump @@ -27,7 +27,7 @@ Download: L<0.3|http://p.blicky.net/h25z8> September 2011. L gained transfer logging features, and I wrote a quick Perl script to fetch some simple statistics from -it. L (L<0.1|http://p.blicky.net/agolr>). +it. L (L<0.2|http://p.blicky.net/eu00a>, L<0.1|http://p.blicky.net/agolr>). =head2 json.mll diff --git a/dat/ncdc b/dat/ncdc index ebeaaf7..7448e75 100644 --- a/dat/ncdc +++ b/dat/ncdc @@ -49,10 +49,11 @@ L - L - L - L - -L +L - +L I have a few old packages on the L, +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. =back -=cut - -[html]É - =head2 Features Common features all modern DC clients (should) have: diff --git a/dat/ncdu b/dat/ncdu index bcd92e1..848aa5f 100644 --- a/dat/ncdu +++ b/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) I also have convenient static binaries for Linux -L and -L. Download, +L and +L. Download, extract and run; no compilation or installation necessary (uses L). @@ -65,12 +65,16 @@ L. +A port to z/OS is available L. + =head2 Similar projects =over +=item L - Multiple user interfaces. + =item L - Quite similar to ncdu, but a different approach. =item L - Another small ncurses-based disk usage visualization utility. diff --git a/dat/ncdu-changelog b/dat/ncdu-changelog index 7db6875..3a80774 100644 --- a/dat/ncdu-changelog +++ b/dat/ncdu-changelog @@ -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 diff --git a/dat/ncdu-jsonfmt b/dat/ncdu-jsonfmt index b2e35c6..5f95eeb 100644 --- a/dat/ncdu-jsonfmt +++ b/dat/ncdu-jsonfmt @@ -19,17 +19,18 @@ object is an array: =head2 Versioning -The C<< >> and C<< >> 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<< >> and C<< >> 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<< >> 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. 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,C) 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 field is false, otherwise it will consider everything with the same C and empty C 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 is assumed. =back +=head3 Extended information + +In addition, the following fields are exported when I +mode is enabled (available since ncdu 1.13). See the C<-e> flag in L +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. For Linux systems, see +L 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 converted to any specific diff --git a/dat/ncdu-man b/dat/ncdu-man index f752232..3ecd28e 100644 --- a/dat/ncdu-man +++ b/dat/ncdu-man @@ -35,8 +35,8 @@ I 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 @@ -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 + +Select a color scheme. Currently only two schemes are recognized: I to +disable colors (the default) and I for a color scheme intended for dark +backgrounds. + =back =head2 Scan Options diff --git a/dat/ncdu-scr b/dat/ncdu-scr index e65e393..885caf4 100644 --- a/dat/ncdu-scr +++ b/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.] diff --git a/dat/nginx-confgen b/dat/nginx-confgen new file mode 100644 index 0000000..9e72c6f --- /dev/null +++ b/dat/nginx-confgen @@ -0,0 +1,54 @@ +=pod + +nginx-confgen is a simple preprocessor and macro system for +L 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 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 output.conf + +To compile from source, install L and run: + + git clone git://g.blicky.net/nginx-confgen.git + cd nginx-confgen + stack install + +The git repository is also available for L. diff --git a/dat/nginx-confgen-changelog b/dat/nginx-confgen-changelog new file mode 100644 index 0000000..c9a044a --- /dev/null +++ b/dat/nginx-confgen-changelog @@ -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 diff --git a/dat/nginx-confgen-man b/dat/nginx-confgen-man new file mode 100644 index 0000000..d4efeb5 --- /dev/null +++ b/dat/nginx-confgen-man @@ -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 +below. + +B Do NOT use nginx-confgen with untrusted input, the C +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 + +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 + +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 + +Set the search path for I 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 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 directive in nginx, except that variables defined with +C are resolved during preprocessing. Note that variables defined with +C 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 address. With the following hack +you can fetch the nameserver from C and use that as the +C: + + 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 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, 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 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 +directive in I. While nginx-confgen should be able to +handle the I 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. +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 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. + +=item * + +C 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 + +Web: L diff --git a/dat/tuwf b/dat/tuwf index fdc5e8c..300638d 100644 --- a/dat/tuwf +++ b/dat/tuwf @@ -39,10 +39,10 @@ information and details. =head2 Download -B 1.0 ([dllink TUWF-1.0.tar.gz download] +B 1.1 ([dllink TUWF-1.1.tar.gz download] - L) -TUWF is also available on a git repository at L. +TUWF is also available on a git repository at L. =head2 Websites using TUWF @@ -51,13 +51,13 @@ TUWF is also available on a git repository at L. =over -=item * L (the site that spawned TUWF - L) +=item * L (the site that spawned TUWF - L) -=item * L (L) +=item * L (L) -=item * L (L) +=item * L (L) -=item * L (L) +=item * L (L) =item * The website embedded in the L mixing console. diff --git a/dat/tuwf-changelog b/dat/tuwf-changelog index 999709a..cfb7203 100644 --- a/dat/tuwf-changelog +++ b/dat/tuwf-changelog @@ -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: diff --git a/download/TUWF-1.1.tar.gz.asc b/download/TUWF-1.1.tar.gz.asc new file mode 100644 index 0000000..8f764b0 --- /dev/null +++ b/download/TUWF-1.1.tar.gz.asc @@ -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----- diff --git a/download/TUWF-1.1.tar.gz.md5 b/download/TUWF-1.1.tar.gz.md5 new file mode 100644 index 0000000..08ef26a --- /dev/null +++ b/download/TUWF-1.1.tar.gz.md5 @@ -0,0 +1 @@ +4629606ce88c28e2ea767bf7b356fe9c TUWF-1.1.tar.gz diff --git a/download/TUWF-1.1.tar.gz.sha1 b/download/TUWF-1.1.tar.gz.sha1 new file mode 100644 index 0000000..bee79bd --- /dev/null +++ b/download/TUWF-1.1.tar.gz.sha1 @@ -0,0 +1 @@ +7f2c4018674f7009db5d3d30218d379210c3a972 TUWF-1.1.tar.gz diff --git a/download/code/awshrink b/download/code/awshrink new file mode 100644 index 0000000..f4d6f63 --- /dev/null +++ b/download/code/awshrink @@ -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; diff --git a/download/code/bbcode.c b/download/code/bbcode.c new file mode 100644 index 0000000..62a782e --- /dev/null +++ b/download/code/bbcode.c @@ -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 +#include +#include +#include + +#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", "", "", FALSE }, + { "i", "", "", FALSE }, + { "u", "", "", FALSE }, + { "size", "", "", TRUE }, + { "color", "", "", TRUE }, + { "url", "", "", TRUE }, + { "quote", "Quote: ", "", FALSE }, + { "quote", "%s wrote: ", "", TRUE }, + { "img", "\"\"", FALSE }, + { "img", "\"",", TRUE }, + { "email", "", "", TRUE }, + { "list", "
    ", "
", FALSE }, + { "list", "
    ", "
", TRUE }, + { "code", "Code: ", "", 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, ""); + else + pi->liststart = pi->inlist; + fprintf(pi->dest, "
  • "); + + return; /* no need to parse more */ + } + + /* begin a tag */ + if(*pi->tag != '/') { + int i; int got = -1; + for(i=0 ; itag, 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 ; idest, "%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;iinlist && 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, "
    \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 +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff --git a/download/code/grenamr-0.1.pl b/download/code/grenamr-0.1.pl new file mode 100644 index 0000000..17aa506 --- /dev/null +++ b/download/code/grenamr-0.1.pl @@ -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); +} + + + diff --git a/download/code/mdc2-parse.pl b/download/code/mdc2-parse.pl new file mode 100644 index 0000000..eb45c67 --- /dev/null +++ b/download/code/mdc2-parse.pl @@ -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| + + + + + + + + + + + + + + + \n|; + +printf(qq| + + + + + + + +\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|
    Date/timeTransferedTimeSpeedChunksUserFilename
    %s%s%s%.2f KiB/s%d/%d/%d%s%s
    + +|; + + +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; + } +} diff --git a/download/code/nccolour.c b/download/code/nccolour.c new file mode 100644 index 0000000..2b19d17 --- /dev/null +++ b/download/code/nccolour.c @@ -0,0 +1,99 @@ +/* nccolour.c - Print out some ncurses colours. + * + * Compile using: + * gcc nccolour.c -o nccolour -lncurses + * + * Written by Yoran Heling + * + * Date: 2011-06-11 + * License: MIT + * Web: http://dev.yorhel.nl/dump/nccolour + */ + +#include +#include + + +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 -p + + 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 +#include +#include + +#include +#include +#include +#include +#include +#include + + +#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 0) { + printf(" ...for : "); + for(i=0; i= 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; +} + diff --git a/download/code/yapong-0.01.c b/download/code/yapong-0.01.c new file mode 100644 index 0000000..2ff75a3 --- /dev/null +++ b/download/code/yapong-0.01.c @@ -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 +#include + +#include +#include +#include + +#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; +} + diff --git a/download/code/yapong.c b/download/code/yapong.c new file mode 100644 index 0000000..31bfd55 --- /dev/null +++ b/download/code/yapong.c @@ -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 +#include + +#include + +#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; +} + diff --git a/download/ncdu-1.13.tar.gz.asc b/download/ncdu-1.13.tar.gz.asc new file mode 100644 index 0000000..2af4dee --- /dev/null +++ b/download/ncdu-1.13.tar.gz.asc @@ -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----- diff --git a/download/ncdu-1.13.tar.gz.md5 b/download/ncdu-1.13.tar.gz.md5 new file mode 100644 index 0000000..c8c78bc --- /dev/null +++ b/download/ncdu-1.13.tar.gz.md5 @@ -0,0 +1 @@ +67239592ac41f42290f52ab89ff198be ncdu-1.13.tar.gz diff --git a/download/ncdu-1.13.tar.gz.sha1 b/download/ncdu-1.13.tar.gz.sha1 new file mode 100644 index 0000000..736b770 --- /dev/null +++ b/download/ncdu-1.13.tar.gz.sha1 @@ -0,0 +1 @@ +3233c4185208d9989ac528a94817ed92dd59c773 ncdu-1.13.tar.gz diff --git a/img/ncduconfirm-2.png b/img/ncduconfirm-2.png new file mode 100644 index 0000000..f10cf1b Binary files /dev/null and b/img/ncduconfirm-2.png differ diff --git a/img/ncdudone-2.png b/img/ncdudone-2.png new file mode 100644 index 0000000..0758369 Binary files /dev/null and b/img/ncdudone-2.png differ diff --git a/img/ncduhelp1-2.png b/img/ncduhelp1-2.png new file mode 100644 index 0000000..80b9b5b Binary files /dev/null and b/img/ncduhelp1-2.png differ diff --git a/img/ncduhelp2-2.png b/img/ncduhelp2-2.png new file mode 100644 index 0000000..cca51ee Binary files /dev/null and b/img/ncduhelp2-2.png differ diff --git a/img/ncduinfo-2.png b/img/ncduinfo-2.png new file mode 100644 index 0000000..2076d5f Binary files /dev/null and b/img/ncduinfo-2.png differ diff --git a/img/ncduscan-2.png b/img/ncduscan-2.png new file mode 100644 index 0000000..5b642f8 Binary files /dev/null and b/img/ncduscan-2.png differ diff --git a/index.cgi b/index.cgi index eafc80a..6e3bef5 100755 --- a/index.cgi +++ b/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 {