Tuesday, December 15, 2009

Windows 7 Permissions

I find the way that this dialog is worded very, very funny:

Monday, November 23, 2009

Google funnies

I really think these kinds of things are injected by Google as easter eggs:

Sunday, November 22, 2009

DOMElement innerHMTL

Currently, there's no built-in method in the DOMElement that gets the inner/outerHTML of the element. There are a few solutions in the comments and on other blogs, but they loop through all the childNodes in order to get the innerHMTL. Getting the outerHTML is much easier (no looping) and just as useful: function outerHTML($e) { $doc = new DOMDocument(); $doc->appendChild($doc->importNode($e, true)); return $doc->saveHTML(); } Still, I'm not sure that is the most optimal way of doing it. It seems that DOMDocument::saveXML accepts an optional DOMElement parameter which, when specified, causes the function to return only that element's XML. You could rewrite our outerHTML function like this: function outerXML($e) { return $e->ownerDocument->saveXML($e); }

Wednesday, November 18, 2009

Use ssh-copy-id on a different port

ssh-copy-id has known problems with handling non-standard ports (e.g. connecting to a different port than 22). To overcome this issue, use a command like: $ ssh-copy-id "user@host -p 6842"

Wednesday, November 11, 2009

a + b in MIPS assembly

We're just starting to learn assembly at college, so here's my first program. It reads to numbers, sums them up and displays the result. Not really rocket science, but good for a first program without any previous practice: .data prompt1: .asciiz "a: " prompt2: .asciiz "b: " newline: .asciiz "\n" .text main: # print prompt 1 li $v0, 4 la $a0, prompt1 syscall # read in $t0 li $v0, 5 syscall move $t0, $v0 #print prompt 2 li $v0, 4 la $a0, prompt2 syscall # read in $t1 li $v0, 5 syscall move $t1, $v0 # $a0 = $t0 + $t1 add $a0, $t0, $t1 # print result li $v0, 1 syscall # newline li $v0, 4 la $a0, newline syscall # exit li $v0, 10 syscall References:

Wednesday, November 4, 2009

A brainfuck interpreter

In case you didn't know, there's an esoteric programming language named brainfuck. It is quite simple and I thought I should make an interpreter for it, in C. It is nowhere near the sub-200 bytes compilers others have made, but it's a good exercise. Well, here's my solution: /* A brainfuck intepreter written in C, complete with error checking so you don't hurt yourself while, uh, brainfucking. Nothing really special about the implementation and it is probably very poor performance-wise. Author: Felix Oghină License: (brain)fuck licenses! */ #include <stdio.h> #include <stdlib.h> #include <string.h> // by brainfuck standards (doesn't that sound funny?), the data pointer has // 30,000 bytes at its disposal, but I hate hard-coding such stuff. #define DATA_SIZE 30000 void usage() { puts( "Usage: brainfuck FILE\n" "If FILE is ommited or is '-', standard input is read" ); } int main(int argc, char **argv) { // used by the bf program unsigned char *dataptr = malloc(sizeof(char) * DATA_SIZE); // position of the data pointer unsigned int datapos = 0; // input file FILE *input; // level - deepness of brackets // i - uh, you need explanation for this one? unsigned int level, i; // we will read chars from the input into r unsigned char r; // determine input if (argc == 2) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { usage(); return EXIT_SUCCESS; } else { input = fopen(argv[1], "r"); if (input == NULL) { puts("Error opening input file"); return EXIT_FAILURE; } } } else { usage(); return EXIT_FAILURE; } // zero the data pointer for (i=0; i < DATA_SIZE; i++) { dataptr[i] = 0; } // start interpreting rewind(input); while (!feof(input)) { r = (unsigned char) fgetc(input); switch(r) { case '>': if (datapos < DATA_SIZE - 1) datapos++; else { puts("brainfuck error: pointer overflow"); return EXIT_FAILURE; } break; case '<': if (datapos > 0) datapos--; else { puts("brainfuck error: pointer underflow"); return EXIT_FAILURE; } break; case '+': dataptr[datapos]++; break; case '-': dataptr[datapos]--; break; case '.': putchar(dataptr[datapos]); break; case ',': dataptr[datapos] = getchar(); break; case '[': if (dataptr[datapos] == 0) { level = 1; while (level != 0) { r = (unsigned char) fgetc(input); if (r == '[') level ++; else if (r == ']') level --; } } break; case ']': if (dataptr[datapos] != 0) { level = 1; while (level != 0) { fseek(input, -2, SEEK_CUR); r = (unsigned char) fgetc(input); if (r == ']') level ++; else if (r == '[') level --; } } break; } } return EXIT_SUCCESS; }
Syntax Highlighting by Pygmentool
Download the source code

Well, guess it's time to fill up that swear jar now.

Friday, October 23, 2009

Windows 7 editions

Did you know there's an entire article on Wikipedia about the various Windows 7 editions? Talk about confusing.

Sunday, October 11, 2009

RhythmToWeb moved to Google Code

I have moved the RhythmToWeb project to Google code. Apart from a small bugfix (it used to send empty requests every x seconds when Rhythmbox was first started), I have also added a couple of server-side examples.

Saturday, October 10, 2009

Apache nice URLs without mod_rewrite

I recently noticed a peculiar behavior in Apache. It seems that if I have, for example, a file /var/www/hello.php, the following URLs will load the same file:
  • http://localhost/hello.php
  • http://localhost/hello
  • http://localhost/hello/goodbye/
After a bit of researching, I found that this behavior is dictated by MultiViews, so if you have that option turned on on your server you can practically forget about rewriting URLs and use this instead.

The only situation I can imagine where you would absolutely need mod_rewrite is if you need to have URLs like http://example.com/1234, you can't handle that with MultiViews (but you can handle http://example.com/article/1234).

Monday, October 5, 2009

RhythmToWeb Updated

I've recently received an e-mail from Aaron Hill, about some modifications he's done to RhythmToWeb. I took his idea of storing information about more than one song, and adapted it in my own way. You can now use the buttons under the song information on the right to browse the last 5 songs I've played. It will also automatically refresh, so if you wait around long enough you'll see it switch to a new song when I start playing one (I really don't know who I'm kidding, no one will ever wait on my blog to see when my song changes :P). Anyway, on to the code: PHP, this gets called by Rhythmbox: <?php define('CALLBACK', 'rtw_callback'); define('JS_FILE', 'nowplaying.js'); define('MAX_ENTRIES', 5); define('SERIALIZE_FILE', './data'); function test_value($var) { if ( strlen($var) && mb_strtolower($var) != 'unknown' && $var != '0' ) { return true; } return false; } $song_info = array(); foreach ($_GET as $key => $value) { if (test_value($value)) { $song_info[$key] = $value; } } $last_songs = unserialize(file_get_contents(SERIALIZE_FILE)); if ($last_songs === false) $last_songs = array(); elseif (count($last_songs) >= MAX_ENTRIES) { while (count($last_songs) >= MAX_ENTRIES) { array_shift($last_songs); } } $last_songs[] = $song_info; file_put_contents(SERIALIZE_FILE, serialize($last_songs)); file_put_contents(JS_FILE, CALLBACK . '(' . json_encode($last_songs) . ')'); ?>
Syntax Highlighting by Pygmentool
HTML, this is in the HTML widget on my blog: <div id="rtw_info">Loading...</div> <button style="padding: 2px 3px; font-size: 0.6em; background: #454545; border: solid 1px #7f7f7f; color: #fff; font-weight: bold; float: right" onclick="rtw_newer()" title="Show more recent songs">&gt;</button> <button style="padding: 2px 3px; font-size: 0.6em; background: #454545; border: solid 1px #7f7f7f; color: #fff; font-weight: bold" onclick="rtw_older()" title="Show older songs">&lt;</button> <script type="text/javascript"> rtw_songs = null; rtw_curIndex = 0; rtw_script_url = "http://znupi.no-ip.org/felix/nowplayingv2/nowplaying.js"; function rtw_callback(aSongs) { // store the received data and show the last song played if (rtw_songs == null || rtw_songs[0].title != aSongs[0].title) { rtw_songs = aSongs; rtw_curIndex = aSongs.length - 1; rtw_update(); } } function rtw_refresh() { var script = document.createElement('script'); script.src = rtw_script_url + "?" + Math.random(); document.body.appendChild(script); setTimeout(rtw_refresh, 5000); } function rtw_older() { if (rtw_songs === null) return; if (rtw_curIndex > 0) { rtw_curIndex --; rtw_update(); } } function rtw_newer() { if (rtw_songs === null) return; if (rtw_curIndex < rtw_songs.length - 1) { rtw_curIndex ++; rtw_update(); } } function rtw_update() { // update the DOM var toFill = document.getElementById('rtw_info'); // first, clear everything in the div while (toFill.childNodes.length) { toFill.removeChild(toFill.childNodes[0]); } // now fill it according to what data we have var curSong = rtw_songs[rtw_curIndex]; // no data: if (curSong.length == 0) { toFill.appendChild(document.createTextNode('Nothing currently playing.')); } // some data: else { var b; if (curSong.title) { b = document.createElement("b"); b.appendChild(document.createTextNode("Song: ")); toFill.appendChild(b); toFill.appendChild(document.createTextNode(curSong.title)); toFill.appendChild(document.createElement("br")); } if (curSong.artist) { b = document.createElement("b"); b.appendChild(document.createTextNode("By: ")); toFill.appendChild(b); toFill.appendChild(document.createTextNode(curSong.artist)); toFill.appendChild(document.createElement("br")); } if (curSong.album) { b = document.createElement("b"); b.appendChild(document.createTextNode("From: ")); toFill.appendChild(b); toFill.appendChild(document.createTextNode(curSong.album)); toFill.appendChild(document.createElement("br")); } if (curSong.genre) { b = document.createElement("b"); b.appendChild(document.createTextNode("Genre: ")); toFill.appendChild(b); toFill.appendChild(document.createTextNode(curSong.genre)); toFill.appendChild(document.createElement("br")); } if (curSong.year) { b = document.createElement("b"); b.appendChild(document.createTextNode("Year: ")); toFill.appendChild(b); toFill.appendChild(document.createTextNode(curSong.year)); toFill.appendChild(document.createElement("br")); } if (curSong.duration) { b = document.createElement("b"); b.appendChild(document.createTextNode("Length: ")); toFill.appendChild(b); var len = parseInt(curSong.duration); var mins = Math.floor(len / 60); var secs = len % 60; toFill.appendChild(document.createTextNode(mins + ":" + secs)); toFill.appendChild(document.createElement("br")); } } } rtw_refresh(); </script>
Syntax Highlighting by Pygmentool
Pretty kewl, eh?

Thursday, September 24, 2009

Android Market Share

@wbm Agreed that android needs more market share before you should care.
Do you think that's true? I beg to differ. Getting your foot in early can earn you recognition on a new platform pretty easily. Let me explain: if you were to develop some awesome app for the iPhone right now, you would find it at the bottom of an over saturated App Store, amongst mostly mediocre products which no one bothers to browse. Most iPhone users just install Facebook, some Twitter client and a couple of silly games. Instead, the Android Market is hungry for new apps (not only because it's new, but also because of the emphasis that Google puts on third-party development and the general openness of the platform), and getting a head start can really make a difference. Your application gets a lot more exposure on the Android Market and the chances of it becoming a hit later are much more increased. Sure, it's a bit more risk, but I think it's worth it.

Tuesday, September 22, 2009

Java: parse XML from the web smartly

Up until recently, I used to fetch the entire XML document before parsing it. I've found that that can be extremely unhealthy for your application, especially if you're developing on Android, where resources are scarce and the GC is very unforgiving. The optimal way to parse XML is to parse it while it's loading, so that resources are freed much more efficient. Here's a snippet of code that demonstrates how to do this: URL oUrl = new URL(sUrl); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); MyXMLHandler handler = new MyXMLHandler(); xr.setContentHandler(handler); xr.parse(new InputSource(oUrl.openStream()));
Syntax Highlighting by Pygmentool
This way, XML is parsed as it comes in, and the application is much faster and smoother. Also, if you don't parse the whole XML this is an even greater improvement, because you can stop parsing when you have enough data (by throwing a SAXException) and it will even stop loading data, which results in faster load times and less bandwidth usage.

Sunday, September 20, 2009

Java Package Mayhem

Here's how a 350-some line Android Activity looks like, in terms of imports: import java.io.StringReader; import java.net.URLEncoder; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.TextView; import com.tastekid.TasteKid.TasteKidUtils.Resource; import com.tastekid.TasteKid.TasteKidUtils.ResourceList; Thank God for Eclipse's Ctrl+Shift+O (yes, I know I bashed Eclipse before, but it seems to behave much more nicely under Ubuntu than under Windows -- it doesn't go above 300MB RAM usage).

Fetch HTTP content in Java

Since programming for Android, I hit my head against every possible snag in the Java programming language. For example, I have to fetch the content of a URL. In PHP, I'd simply do: $data = file_get_contents($url); But no, in Java, no such easiness for you! I had to write my own helper function: public static String getUrlContent(String sUrl) throws Exception { URL url = new URL(sUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setDoOutput(true); connection.setConnectTimeout(5000); connection.setReadTimeout(5000); connection.connect(); BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream())); String content = "", line; while ((line = rd.readLine()) != null) { content += line + "\n"; } return content; } Seems very hackish, especially the line starting with BufferedReader. The whole function is actually composed of bits of code found around the web. Bah, why didn't Google choose Python as their default programming language for Android?

Monday, September 14, 2009

SVN for an Android project

I've started working on the Android project, and I'm using SVN as my version control system (I'm the only one working on it, so I'm actually using it more as a backup and for keeping track of things). A small tip when doing this is to not simply add everything in your project directory to the repository. In one word, don't run: $ svn add * I did so, and three revisions later I was unable to commit anymore, because Eclipse had removed some of its automatically generated files and svn didn't know what to do with them. Here's what you have to have in your repository: $ svn ls .@1 AndroidManifest.xml TODO default.properties res/ src/ You don't need the rest.

Thursday, September 10, 2009

Spam

Funniest spam I ever got: Hello My Dear How are you? i hope all is well with you, i hope you may not know me, and i don\'t know who you are, My Name is Miss morin khalifa i am just broswing now i just saw your email it seams like some thing touches me all over my body, i started having some feelings in me which i have never experience in me before, so i became interested in you, l will also like to know you the more,and l want you to send an email to my email aaddress so l can give you my picture for you to know whom l am. I believe we can move from here! I am waiting for your mail to my email address above. (Remeber the distance or colour does not matter but love matters alot in life) miss morin I guess my e-mail address is so sexy it turned her on O.o

Tuesday, September 8, 2009

Playing with HMTL5 Drag & Drop

I'm just toying around with HTML5-related things, to prepare myself so I don't get smacked in the head when it starts rolling out. The most interesting feature, in my opinion, is support for Drag & Drop. What I find most interesting about this feature is being able to drag & drop files from your desktop / file browser into a webpage. Unfortunately, we are unable to upload files this way just yet (well, it's available in Firefox 3.6 Alpha, so yeah). Anyway, if you have a decent Firefox version (I have 3.5.2 on Ubuntu), you can toy around with this demo app. I built it so I can observe the different event attributes when dropping things in the gray box. This is the first step towards my goal of a fully transparent drag & drop file upload. I guess I just hate those file inputs. Also, don't forget to look at the source. It's pretty small and simple.

Thursday, September 3, 2009

Folding@Home on Ubuntu Server

I recently started folding @ home on my Jaunty-based server (which runs 24/7 with not so much activity). What I have noticed is that they do not provide a proper init.d script for it. This is the default one they tell you to use: # chkconfig: 345 93 14 # description: will start FAH client as a service cd /path/to/folding ./fah6 -verbosity 9 < /dev/null > /dev/null 2>&1 & This doesn't seem to work for me, the process does not start. What I've found to work is this: ### BEGIN INIT INFO # Provides: fah # Required-Start: $local_fs $network $named # Required-Stop: $local_fs $network $named # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start Folding@Home client ### END INIT INFO # chkconfig: 345 93 14 # description: will start FAH client as a service cd /home/felix/fah/ ./fah6 -verbosity 9 < /dev/null > /dev/null 2>&1 & There you go. Now you have no reason to not run this :) Edit: No, it doesn't work. It seems init starts the script twice, which messes things up. I think this has something to do with runlevels, but I'm too tired to figure it out now. If anyone knows a solution, please comment.

Tuesday, September 1, 2009

Verifying a hostname / IP address with PHP

If you ever need to verify a hostname or an IP address in PHP, here's how: // try to determine the IP address of the hostname // if the hostname is actually an IP, gethostbyname() will return it unchanged // if the hostname cannot be resolved, it will have the same behavior $ip = gethostbyname($address); // check if the resulting IP is valid if ($ip !== long2ip(ip2long($ip))) { echo "Invalid hostname or IP address"; }

Monday, August 17, 2009

Captcha

Here's a custom image captcha I just made. What's cool about it is that it's transparent (PNG), so it 'blends' into the background. To suit different backgrounds, it has two settings - dark and light - which you set according to the background color of the page you want to put it on. Here's a picture that demonstrates this; you can see that the captcha is completely unreadable when put on the wrong background, but completely readable when it's right: Captcha Demo Here's the code: <?php $W = 160; // width $H = 60; // height $L = 6; // length of the key $BG = 'light'; // can be 'light' or 'dark', accorting to the background color of // the page it will be on $F = './DejaVuSans.ttf'; // path to true-type font file function makeKey($length) { // generate a random sequence of characters $a = 'abcdefghijklmnopqrstuvwxyz'; $s = ''; for ($i=0; $i < $length; $i++) { $s .= $a[mt_rand(0, strlen($a) - 1)]; } return $s; } $img = imagecreatetruecolor($W, $H); // make the image alpha-aware imagesavealpha($img, true); // make colors 'blend', not overwrite imagealphablending($img, true); // make the image transparent imagefill($img, 1, 1, imagecolorallocatealpha($img, 0, 0, 0, 127)); // generate two random colors and decide which one goes where $dark = Array (mt_rand(0, 126), mt_rand(0, 126), mt_rand(0, 126)); $light = Array (mt_rand(127, 255), mt_rand(127, 255), mt_rand(127, 255)); if ($BG == 'dark') { $bg_color = imagecolorallocatealpha($img, $dark[0], $dark[1], $dark[2], mt_rand(64, 96)); $fg_color = imagecolorallocatealpha($img, $light[0], $light[1], $light[2], mt_rand(32, 64)); } else { $bg_color = imagecolorallocatealpha($img, $light[0], $light[1], $light[2], mt_rand(64, 96)); $fg_color = imagecolorallocatealpha($img, $dark[0], $dark[1], $dark[2], mt_rand(32, 64)); } // write background static $angle = mt_rand(20, 35); for ($i=0; $i < 15; $i++) { imagettftext($img, 12, $angle, 0, $i*15, $bg_color, $F, makeKey(30)); } $key = makeKey($L); // you should store this in the user session to check it later // write the actual key, in two parts imagettftext($img, mt_rand(16, 22), mt_rand(10, 30), mt_rand(5, 30), mt_rand($H-16, $H-22), $fg_color, $F, substr($key, 0, 3)); imagettftext($img, mt_rand(16, 22), mt_rand(-30, -10), mt_rand($W/2+5, $W/2+30), mt_rand(16, 22), $fg_color, $F, substr($key, 3, 3)); // output the image header("Content-Type: image/png"); imagepng($img); ?> On my machine (Pentium DualCore @ 2.80Ghz) it generates images in 70-75ms. I think that's pretty fair. Also, it works with non-bundled GD versions, too, so you don't have to worry about that. Enjoy.

Saturday, August 15, 2009

Ubuntu One

Ubuntu One is a file hosting service provided by Canonical (the company that develops Ubuntu). You are given 2GB for free and 10GB for $10/month. The way it works is that you create an account, install a client and associate it with your account. After that, the client will monitor your $HOME/Ubuntu One folder and synchronize its contents with the Ubuntu One servers. The main idea of it is that of syncing between computers -- you associate all your PCs/laptops with the same account and your files get automatically synced between them. However, I use it for another purpose -- backups. I back up my work and other stuff on Ubuntu One, 2GB is really enough (for me, at least). What's great is that they also provide a web interface to download / upload files from / to your account, so I can access my files wherever, whenever, from whatever. Don't jump head first, though. You might want to have a look at Dropbox, too. The main advantages are:
  • It has clients for more platforms (Windows, Mac, other Linux distros);
  • It is more badwidth efficient, as it only uploads diffs when overwriting a file (I'm not sure whether Ubuntu One does that too, or not);
  • It's a bit more mature
The only reason why I chose Ubuntu One over Dropbox is because I was sure it integrated well with Ubuntu and because I don't use any other OSes. Also, they are still in Beta, so they are constantly improving and adding features. The choice... is yours. [manic laugh]

Pagination with Smarty

I've spent the last hour or two on this. It's a Smarty template that takes an associative array with these keys:
  • curPage: The number of the current page
  • url: The url to which it will make the links. It replaces "%x" with the page number in the URL, so this can be something like /news/page-%x/ or /news.php?page=%x
  • totalPages: The number of total pages.
It also has a few variables that tweak its output (these are set in the template itself, using {assign}):
  • putDots: If the distance between the current page and the first/last page is greater than this, it will put dots towards the end
  • border: when it puts dots around the current page, this is the number of pages that appear around it until the dots start.
I'm too tired and bored to explain anything else, but if anyone has any questions, I'll be glad to answer them. Here's the template: {assign var="putDots" value=3} {assign var="border" value=2} {assign var="curPage" value=$pagination.curPage} {assign var="url" value=$pagination.url} {assign var="totalPages" value=$pagination.totalPages} {if $totalPages > 1} <div class="pages"> <span> {if $curPage > 1} <a title="Previous Page" href="{$url|replace:'%x':$curPage-1}">&laquo;&laquo;</a> {/if} {* Handle the first part of the pages -- up to the current one *} {if $curPage > $putDots} <a title="Page 1" href="{$url|replace:'%x':'1'}">1</a> ... {section name=i start=$curPage-$border loop=$curPage} {assign var="curPos" value=$smarty.section.i.index} <a title="Page {$curPos}" href="{$url|replace:'%x':$curPos}">{$curPos}</a> {/section} {else} {section name=i start=1 loop=$curPage} {assign var="curPos" value=$smarty.section.i.index} <a title="Page {$curPos}" href="{$url|replace:'%x':$curPos}">{$curPos}</a> {/section} {/if} {* Current page *} <a title="Page {$curPage}" class="current" href="{$url|replace:'%x':$curPage}">{$curPage}</a> {* Handle the last part of the pages -- from the current one to the end *} {if $totalPages - $curPage + 1 > $putDots} {section name=i start=$curPage+1 loop=$curPage+$border+1} {assign var="curPos" value=$smarty.section.i.index} <a title="Page {$curPos}" href="{$url|replace:'%x':$curPos}">{$curPos}</a> {/section} ... <a title="Page {$totalPages}" href="{$url|replace:'%x':$totalPages}">{$totalPages}</a> {else} {section name=i start=$curPage+1 loop=$totalPages+1} {assign var="curPos" value=$smarty.section.i.index} <a title="Page {$curPos}" href="{$url|replace:'%x':$curPos}">{$curPos}</a> {/section} {/if} {if $curPage < $totalPages} <a title="Next Page" href="{$url|replace:'%x':$curPage+1}">&raquo;&raquo;</a> {/if} </span> </div> {/if}

Friday, August 14, 2009

PHP Server Uptime

This is a pretty simple way of getting the server uptime using PHP. Note that this only works on Linux (and probably other Unix-like OSes that store the machine uptime in /proc/uptime). Straight to the code: function get_uptime() { $file = @fopen('/proc/uptime', 'r'); if (!$file) return 'Opening of /proc/uptime failed!'; $data = @fread($file, 128); if ($data === false) return 'fread() failed on /proc/uptime!'; $upsecs = (int)substr($data, 0, strpos($data, ' ')); $uptime = Array ( 'days' => floor($data/60/60/24), 'hours' => $data/60/60%24, 'minutes' => $data/60%60, 'seconds' => $data%60 ); return $uptime; }

Thursday, August 13, 2009

GeoIP MySQL

A while ago I found this great article on how to import the free GeoIP database into MySQL. It provides a really simple way to look up IPs and see what country they are from using a MySQL database. It is also fairly optimized for size (the GeoIP .csv is 7.9MB and the MySQL tables are 1.9MB). Vincent (the author of the article) also provides some PHP snippets that look up IPs, just to get the feel of it: <?php function getALLfromIP($addr,$db) { // this sprintf() wrapper is needed, because the PHP long is signed by default $ipnum = sprintf("%u", ip2long($addr)); $query = "SELECT cc, cn FROM ip NATURAL JOIN cc WHERE ${ipnum} BETWEEN start AND end"; $result = mysql_query($query, $db); if((! $result) or mysql_numrows($result) < 1) { //exit("mysql_query returned nothing: ".(mysql_error()?mysql_error():$query)); return false; } return mysql_fetch_array($result); } function getCCfromIP($addr,$db) { $data = getALLfromIP($addr,$db); if($data) return $data['cc']; return false; } function getCOUNTRYfromIP($addr,$db) { $data = getALLfromIP($addr,$db); if($data) return $data['cn']; return false; } function getCCfromNAME($name,$db) { $addr = gethostbyname($name); return getCCfromIP($addr,$db); } function getCOUNTRYfromNAME($name,$db) { $addr = gethostbyname($name); return getCOUNTRYfromIP($addr,$db); } ?> If anyone needs this, I have exported the cc and ip tables from the 01-May-09 version of the GeoIP database (it's the latest one at this point in time). Download: geoip.01-May-2009.sql.gz [773.5 KB] Also, here's a little demo application that looks up IPs and/or hostnames: http://znupi.no-ip.org/felix/work/2/ip-lookup/ (which might be offline at times)

Wednesday, August 12, 2009

Ubuntu: Home Sweet Home

Back to Ubuntu from my temporary Windows usage. Man, it's good to be back. Have to go restore all my backups now, buh bye.

Thursday, August 6, 2009

Notepad++ and Python

I know, I know, I'm using Windows. But it's just temporary, I just felt like playing some video games. Anyway, Notepad++ is awesome, I must say. And I wanted to use it for Python developing (I hate that IDLE Python comes with). To set up Notepad++ for Python developing I had to:
  • Create a batch script that would run a python script and then wait for a key (so that the terminal (or, command line) doesn't disappear);
  • Configure a shortcut in Notepad++ to run that script with the current file as parameter.
First things first: the batch script. Pretty basic, I just looked at a couple of Wikipedia articles and another one about using arguments in batch scripts: @ECHO OFF C:\Python26\python.exe "%1" echo. PAUSE @ECHO ON Then, in Notepad++, I went to Run → Run..., type in C:\Python26\python.bat "$(FULL_CURRENT_PATH)", then Save... and assign a shortcut. Press that shortcut now with a python file opened and boom goes the dynamite. Enjoy :)

Rant on Eclipse

If you at least once thought about programming in Java, you must have heard about Eclipse. Well, I'm playing around with the Android SDK, learning Java at the same time. While using Eclipse to code for Android is very easy and straightforward, Eclipse itself is a monstrous bloat of an application. I barely have one project and five files open and Eclipse is using up 400MB of memory. Don't believe me? Check for yourself: Quite annoying. And this is nothing, one time it happened that Eclipse was using 700MB with only a couple of files open. I'm not sure whether this is Eclipse's fault or Java's fault, but I think it's a bit of both. Eclipse's fault is that it's a huge application with tons of features you will never ever use. Java's fault is that it was never designed to run desktop applications (in my opinion). Because Java runs in a virtual machine, it can not give memory back to the host OS, so it just keeps using more and more, until it explodes, creating a black hole in your motherboard where the memory chips used to live. Bah, end of rant.

Wednesday, August 5, 2009

Small bandwidth optimization trick

I've been helping my brother out with his project (http://tastekid.com) and learning some new tricks in the meantime. One thing that bugged me is that we have a lot of JavaScript files, because we like to keep things separated (one file for the tooltip, one for the autocomplete feature etc.). While this makes developing easier, it's pretty bad for production because it drastically increases the number of requests made by a client. The solution I came up with is a small PHP script that concatenates all the scripts in to one and minimizes everything using JSMin (ported to PHP). This reduces the number of requests to one and lowers bandwidth usage. The code is pretty straight forward: <?php /** * JS on-the-fly compressor with caching * * Uses JSMin by Douglas Crockford * * Author: Felix Oghina * */ //-- Configuration --// $JSMin = 'jsmin-1.1.1.php'; // path to the JSMin class file $path = '.'; // this can be made dynamic by assigning a value from $_GET, although it may be unsafe $cache = 'js.cache'; // this file will be used as cache. If it's in the same directory as $path, // it should not end in .js //-- End of configuration --// // include the JSMin script require_once $JSMin; // first decide if we use the cache or not $usecache = true; $files = glob($path . '/*.js'); $maxtime = filemtime($files[0]); foreach ($files as $file) { $curtime = filemtime($file); if ($maxtime < $curtime) $maxtime = $curtime; } if (!file_exists($cache)) { $usecache = false; } elseif (filemtime($cache) < $maxtime) { $usecache = false; } // send appropiate headers header("Content-Type: text/javascript"); // we use the cache if ($usecache) { readfile($cache); } // we rebuild the cache else { $js = ''; foreach ($files as $file) { $js .= file_get_contents($file); } $js = JSMin::minify($js); // rewrite the cache file_put_contents($cache, $js); // output the js echo $js; } // done ?> This solution uses caching, so it only minifies after you change something in one of the JavaScript files. In our case, this reduces the number of requests from 5 to 1 and the total size by 6kb (that's six thousand characters). The only flaw (that I see) is that if you delete one of your JavaScript files, it won't update the cache. Although I see the problem, I don't see an immediate solution, so I won't bother with it. It's not like we're going to delete JavaScript files all the time.