2012年8月4日 星期六

An Introduction To The SQLite C/C++ Interface


為了能夠用SQL將資料由資料庫撈出,我們必須認識兩個主要的Object:
  1. The database connection object:sqlite3
  2. 執行sqlite3_open()之後,就會得到該object,感覺類似open之後的file descriptor。
  3. The prepared statement object:sqlite3_stmt
  4. 把SQL轉成bytecode,但是還沒真正被執行。請參考Compiling An SQL Statement/To execute an SQL query, it must first be compiled into a byte-code program using one of these routines.
    An instance of this object represents a single SQL statement. This object is variously known as a "prepared statement" or a "compiled SQL statement" or simply as a "statement".


一個常見的SQLite pattern為:
  1. sqlite3_open():取得database connection object。
  2. sqlite3_preapre() :取得prepared statement object。
  3. sqlite3_step():執行prepared statement object。
  4. sqlite3_column_<type>():將sqlite回傳回來的資料,轉成對應的type取出。
  5. sqlite3_finalize():釋放prepared statement object。
  6. sqlite3_close():釋放database connection object。


#include <iostream>
#include <sqlite3.h>
#include <cstdlib>
#include <assert.h>

using namespace std;

int main(int argc, char** argv)
{
  sqlite3 *conn;
  sqlite3_stmt *statement; // SQL Statement Object
  int ret = 0;
  int cols;

  // This routine opens a connection to an SQLite database file
  //  and returns a database connection object.
  ret = sqlite3_open_v2("hello.db", &conn, SQLITE_OPEN_READONLY, NULL);
  if (ret) {
    cout << "can not open database\n";
    exit(0);
  }

  ret = sqlite3_prepare_v2(conn, "select * from hello", -1, &statement, NULL);
  if (ret != SQLITE_OK) {
    cout << "We did not get any data\n";
    exit(0);
  }

  cols = sqlite3_column_count(statement);

  for (int col = 0; col < cols; col++) {
    cout << " " << sqlite3_column_name(statement, col);
  };
  cout << endl;

  while (true) {
    ret = sqlite3_step(statement);
    if (ret == SQLITE_ROW) {
      for (int col = 0; col < cols; col++) {
        switch (sqlite3_column_type(statement, col)) {
          case SQLITE_INTEGER:
            cout << " " << sqlite3_column_int(statement, col) << " ";
            break;
          case SQLITE_FLOAT:
            cout << " " << sqlite3_column_double(statement, col) << " ";
            break;
          case SQLITE_TEXT:
            cout << " " << sqlite3_column_text(statement, col) << " ";
            break;
          case SQLITE_NULL:
            cout << " " << "NULL" << " ";
            break;
        }
      };
      cout << endl;
    } else if (ret == SQLITE_DONE) {
      cout << "done" << endl;
      break;
    } else {
      cout << "ret:" << ret << endl;
      break;
    }
  }

  sqlite3_finalize(statement);
  sqlite3_close(conn);

  return 0;
}


sqlite3_exec()是將sqlite3_prepare_v2(), sqlite3_step()和 sqlite3_finalize()包裝起來的API,其原型為
int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

  • 第二個參數要執行的SQL statement。
  • 第三個參數是callback function。每一個row都會呼叫該callback一次。
  • 第四個參數是要傳給callback function的data。
  • 第五個參數就是發生錯誤時,儲存錯誤資訊的指標。
因此上面的程式可以改寫為
#include <iostream>
#include <sqlite3.h>

using namespace std;

int callback(void *data, int column, char **value, char **name)
{
  int i;
  cout << (char *)data << endl;
  for (i = 0; i < column; i++) {
    cout << name[i] << ":" << (value[i] ?: "NULL") << endl << flush;
  }
  return 0;
}


int main(int argc, char** argv)
{
  sqlite3 *conn;
  int ret = 0;
  char *errmsg = NULL;
  char data[] = "brook";

  // This routine opens a connection to an SQLite database file
  //  and returns a database connection object.
  ret = sqlite3_open_v2("hello.db", &conn, SQLITE_OPEN_READONLY, NULL);
  if (ret != SQLITE_OK) {
     cout << sqlite3_errmsg(conn) << ".(" << ret << ")" << endl;
     exit(0);
  }

  ret = sqlite3_exec(conn, "select * from hello", callback, data, &errmsg);
  if (ret != SQLITE_OK) {
     cout << "We did not get any data. " << errmsg << endl;
     sqlite3_free(errmsg);
  }

  sqlite3_close(conn);

  return 0;
}






2012年7月28日 星期六

Introduction SQLite


大家比較耳熟能詳的database大多是client/server的架構居多,如:MySQL、PostgreSQL、MS-SQL和Oracle等等,而SQLite感覺比較像MS的Access, 程式本身就負責開啟資料庫並且直接操作,更多的資訊可以參考官網http://www.sqlite.org/

以下就個人在SQLite網站看到的介紹,寫下一些筆記。
About SQLite
SQLite不像許多SQL資料庫有分開的Server process。SQLite直接對資料庫檔案做存取,這些資料庫檔案室可以跨平台的, 意味著您可以直接在32/64和big-endian/little-endian複製這些資料庫檔案,都能正確地被SQLite所存取。

Appropriate Uses For SQLite/Situations Where Another RDBMS May Work Better SQLite因為直接對database做存取,所以多個client同時對某database做存取可能會有問題。 如果是高流量(High-volume Websites)或是高資料量(Very large datasets)都不適合SQLite,畢竟它是拿來給embedded用的。

總體而言,對於小資料庫的應用SQLite已經是非常好的選擇了。


基本操作
brook:~$ ls hello.db
ls: cannot access hello.db: No such file or directory
brook:~$ sqlite hello.db
Loading resources from /home/brook/.sqliterc
SQLite version 2.8.17
Enter ".help" for instructions
sqlite> .help
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in a text format
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.indices TABLE         Show names of all indices on TABLE
.mode MODE             Set mode to one of "line(s)", "column(s)",
                       "insert", "list", or "html"
.mode insert TABLE     Generate SQL insert statements for TABLE
.nullvalue STRING      Print STRING instead of nothing for NULL data
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.schema ?TABLE?        Show the CREATE statements
.separator STRING      Change separator string for "list" mode
.show                  Show the current values for various settings
.tables ?PATTERN?      List names of tables matching a pattern
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM NUM ...     Set column widths for "column" mode

sqlite> .tables
sqlite> create table hello (x integer PRIMARY KEY ASC, y);
sqlite> .tables
hello
sqlite> insert into hello (y) values('a');
sqlite> insert into hello (y) values(10);
sqlite> insert into hello (y) values(datetime('now'));
sqlite> select * from hello;
x           y
----------  ----------
1           a
2           10
3           2012-07-30
sqlite> .quit


我們也可以直接在command line上面執行sqlite
brook:~$ sqlite hello.db .dump
Loading resources from /home/brook/.sqliterc
BEGIN TRANSACTION;
create table hello (x integer PRIMARY KEY ASC, y);
INSERT INTO hello VALUES(1,'a');
INSERT INTO hello VALUES(2,10);
INSERT INTO hello VALUES(3,'2012-07-30 04:20:12');
COMMIT;
brook:~$ sqlite hello.db "select * from hello"
Loading resources from /home/brook/.sqliterc
x           y
----------  ----------
1           a
2           10
3           2012-07-30
brook:~$ cat ~/.sqliterc
.mode column
.header on
.nullvalue NULL

更多SQL語法請參考http://www.sqlite.org/lang.html





2012年6月30日 星期六

Pipe to a Subprocess


偶爾想偷懶的我會在code中使用system(),呼叫shell來執行一些東西,並且將執行完的結果丟回來程式中處理,這時候popen就很好用了,popen - pipe stream to or from a process。
popen允許開啟read或write pipe,不能夠同時開啟為read/write,read可以讓程式從popen中讀回來處理,而write可以將程式中的資料丟到popen處理。

popen(,"r")
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char buf[128];
    float a, b, c, d;
    FILE *fp;
    if ((fp = popen("(df -h 2>/dev/null) | tail -n +2", "r")) == NULL) {
        fprintf(stderr, "popen() failed\n");
        return -1;
    }
    while (!feof(fp)) {
        fscanf(fp, "%*[^ ]%f%*[^1-9]%f%*[^1-9]%f%*[^1-9]%f%%%*[^\n]", &a, &b, &c, &d);
        printf("%f/%f/%f/%f\n", a, b, c, d);
    }
    return 0;
}


popen(,"w")
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char buf[] = "1 2 3 4 5";
    float a, b, c, d;
    FILE *fp;
    if ((fp = popen("xargs -n 1 > /tmp/xx", "w")) == NULL) {
        fprintf(stderr, "popen() failed\n");
        return -1;
    }
    fprintf(fp, "%s", buf);
    pclose(fp);
    return 0;
}


    參考資料:
  • The GNU C Library, Chapter 15.2




2012年5月26日 星期六

Design Patterns - Command pattern


Command pattern是一種將Action封裝起來,並提供一個general的interface作為呼叫時(invoke)的統一介面。

舉個生活例子來說明,想必您一定用過萬用遙控器,User可以設定遙控器上某個按鈕1的功能為電視的開關,所以當User按下按鈕1,就可以開關電視,接著User設定按鈕2為控制冷氣的開關,所以當User按下按鈕2,就可以開關冷氣,這樣簡單的運作就是一種Command Pattern。
電視開關是一種Action,被封裝成遙控器上面的按鈕,對User來說,這是一個General的interface,而且User在變更任和按鈕功能時,並不會影響遙控器上面的其他按鈕。

Command pattern有三個名詞需要解釋一下:
client:The client instantiates the command object and provides the information required to call the method at a later time.
invoker: The invoker decides when the method should be called.
receiver: The receiver is an instance of the class that contains the method's code.

client就可以想成"設定按鈕"的動作,invoker就是User了,而receiver就是電器了。

圖取自http://en.wikipedia.org/wiki/Command_pattern.


#include <iostream>

class Command {
public:
    virtual int exec(void *args) = 0;
};

// Receiver
class TV {
    bool turn_on_off;
public:
    void pwr_switch() {
        if (turn_on_off) {
            std::cout << "Turn off TV" << std::endl;
        } else {
            std::cout << "Turn on TV" << std::endl;
        }
        turn_on_off = !(turn_on_off);
    }
};

class CmdTvSwitch: public Command {
    TV *_tv;
public:
    CmdTvSwitch(TV *tv) {
        this->_tv = tv;
    }
    virtual int exec(void *args) {
        this->_tv->pwr_switch();
        return 0;
    }
};

int main(int argc, char *argv[])
{
    // Client
    TV tv;
    Command  *c;
    c = new CmdTvSwitch(&tv);
    c->exec(NULL);
    c->exec(NULL);
    return 0; 
}

就目前個人在使用這個Pattern上面的感觸就是"Command pattern是一種將Action封裝起來,並提供一個general的interface作為呼叫時(invoke)的統一介面。"比如,我在寫Message Queue會用到,提供新的指令供外界呼叫也會用到。
    參考資料:
  1. http://en.wikipedia.org/wiki/Command_pattern
  2. http://caterpillar.onlyfun.net/Gossip/DesignPattern/CommandPattern.htm




2012年5月19日 星期六

理財常用的Link



股匯市原物料期貨報價, http://www.stockq.org/
全球股市排行榜, http://www.stockq.org/market/
貨幣各國匯率歷史紀錄, http://fxtop.com/en/historates.php
金磚四國和台灣股市走勢圖, http://finance.yahoo.com
台灣地區銀行利率, http://www.taiwanrate.com/
台灣地區各網路銀行入口, http://www.easyatm.com.tw/playatm.html
文茜世界財經週報, http://www.youtube.com
57金錢爆, http://www.youtube.com
國際主要股市月報 , http://www.twse.com.tw/ch/statistics/statistics.php
基金績效,
台股上市公司本益比, http://www.twse.com.tw
台股上櫃公司本益比, http://www.otc.org.tw
基金定期定額試算, http://fund.scsb.com.tw
STOCK-AI提供各類經濟指標數據, https://stock-ai.com/
blog
法意聯盟, http://www.wretch.cc/blog/joejoejoe



持續更新中...



2012年4月7日 星期六

常用的regular expression


這篇會慢慢增加內容,所以我也會更新發布日期。
JavaScript
    var mac = /^\s*([\d[a-f]{2}:){5}[\d[a-f]{2}\s*$/i;
    var ip = /^((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))$/;


C language
    char *mac =  "([[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}";
    char *ip = "^(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))$";


    參考資料
  1. http://en.wikipedia.org/wiki/Regular_expression



2012年4月1日 星期日

頁面遮罩


我們常常可以在Web上看到,當有事件在背景執行時,Web會跳出半透明的視窗遮住後面的頁面,如:


這裡主要是利用JavaScript控制layer(Z-Index),以及style的opacity和style的display達成,layer控制element之間的stack,也就是MS office裡面常常提到的,"往上/下一層",style.opacity則是透明度,style.display控制顯示或隱藏,這些就可以達成頁面遮罩的功能了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <script type="text/javascript">
//<![CDATA[
    function show() {
      document.getElementById("xie").style.display = "";
      document.getElementById("content1").style.display = "";
      document.getElementById("content1").innerHTML = "內容<br/><input onclick='hide()' type='button' value='確定'/>";
    }

    function hide() {
      document.getElementById("xie").style.display = "none";
      document.getElementById("content1").style.display = "none";
    }
  //]]>
  </script>
  <title>JavaScript Mask</title>
</head>

<body>
  <div style=
  "filter:alpha(opacity=50); -moz-opacity:0.5; opacity: 0.5; width: 100%; background-color: White; display: none; height: 100%; position: absolute; left: 0; top: 0;"
       id="xie"></div>

  <div style=
  "width: 100px; background-color: Red; display: none; height: 53px; position: absolute; left: 144px; top: 100px;"
       id="content1"></div>

  <input onclick="show()" type="button" value="顯示" />
</body>
</html>





2012年3月24日 星期六

動態產生HTML element -- document.createElement


我們可以利用document.createElement()動態的產生HTML的element,再利用Node.appendChild()插入某個Node底下,或利用Node.removeChild()自某個Node底下移除,更多資訊可以參考[1]。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>auto table</title>
</head>

<body>
  <table id="brook" summary=""></table>
  <script type="text/javascript">
  //<![CDATA[
    function create_new_row_button(eT, msg) {
        var eR = document.createElement("tr");
        var eC = document.createElement("td");
        var eB = document.createElement("input");
        eB.type = "button"
        eB.value= msg;
        eB.style.width = "100px";

        eT.appendChild(eR);
        eR.appendChild(eC);
        eC.appendChild(eB);
    }
    var station = [
        {name: "CHT"},
        {name: "TW"},
    ];
    for (var i = 0; i < station.length; i++) {
        create_new_row_button(document.getElementById("brook"),
                              station[i].name);
    }
  //]]>
  </script>
</body>
</html>






2012年3月3日 星期六

glibc讀書心得 -- Ch2 Error Reporting


在glibc中會去偵錯並且回傳錯誤值,通常我們都應該去檢查這些回傳值,並且作error-handling,比如開檔 open()/fopen(),並非每次都會開成功,所以,open()/fopen()通常都會去檢查return value是不是有正確。

2.1 Checking for Errors
glibc的return value通常是-1、NULL或是EOF,這只能知道錯誤發生,至於發生何種錯誤,都是存在errno這個變數中,當您要使用errno必須include <errno.h>。這個header file也定義很多error code,都是以E為開頭,這些error code在Ch 2.2中有說明,用到再查就OK啦。

2.2 Error Codes
這個章節主要說明定義的error code,值得注意的是EAGAIN和EWOULDBLOCK的值一樣,所以不能放在同一個case switch中,其實我常用的error code大概只有他列的1/5不到吧,看看就好。

2.3 Error Messages 基本上我們都是希望report的錯誤是文字型態,而不是只有error code,所以glibc有提供一些轉換的function:
char * strerror(int errnum)[Function]
char * strerror_r(int errnum, char *buf, size t n)[Function]
void perror(const char *message)[Function]
char * program_invocation_name[Variable]
char * program_invocation_short_name[Variable]
void error (int status, int errnum, const char *format, . . . )[Function]
error_at_line (int status, int errnum, const char *fname,unsigned int lineno, const char *format, . . . )[Function]

char * strerror(int errnum)
將errnum轉成相對應的字串
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    printf("%s\n", strerror(EINTR));
    // print out "Interrupted system call"
    return 0;
}

char * strerror_r(int errnum, char *buf, size t n)
strerror的reentrant版本,mutli-thread就一定要用這個版本。
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    char buf[32];
    printf("%s\n", strerror_r(EINTR, buf, sizeof(buf)));
    // print out "Interrupted system call"
    return 0;
}

void perror(const char *message)
效果和printf(stderr, "%s:%s", message, strerror(errno))相似,把error message寫到stderr去,接著冒號後面再接errno的message。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    perror("my_perror_msg1");
    // print out "my_perror_msg1: Success"
    errno = EINTR;
    perror("my_perror_msg2");
    // print out "my_perror_msg2: Interrupted system call"
    return 0;
}

char * program_invocation_name / char * program_invocation_short_name
該程式的名稱,和argv[0]的值一樣。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    printf("%s\n", argv[0]);
    // print out "./a.out"
    printf("%s\n", program_invocation_name);
    // print out "./a.out"
    printf("%s\n", program_invocation_short_name);
    // print out "a.out"
    return 0;
}


void error (int status, int errnum, const char *format, . . . )
status不為0時,就等同exit(status),後面的參數errnum和format用途類似perror(),當errnum為0時,就不會印後面的message,但是perror()會印Success。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    error(0, 0, "%s", "msg");
    error(0, EINTR, "%s", "msg");
    // print out "./a.out: msg: Interrupted system call"
    error(1, EINTR, "%s", "msg");
    // print out "./a.out: msg: Interrupted system call" and exit(1)
    return 0;
}


error_at_line(int status, int errnum, const char *fname, unsigned int lineno, const char *format, ...)
error_at_line()和error()很像,只是多了fname和lineno印出檔案名稱和行號。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    error_at_line(0, 0, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:7: msg"
    error_at_line(0, EINTR, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:8: msg: Interrupted system call"
    error_at_line(1, EINTR, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:11: msg: Interrupted system call"
    return 0;
}



    參考資料:
  • The GNU C Library, Chapter 2



IE8無法下載檔案


話說有一天,我用Luci寫一段download動態產生的檔案時,發生部分的IE8下載有問題,都會產生無法下載的訊息,當然就是立刻請問google大神,尋得此文章[PHP]下載檔案時無法直接開啟文件的解法方法,雖然是用PHP寫,不過小改一下就可以在Luci上面如法炮製啦。

[PHP]下載檔案時無法直接開啟文件的解法方法
header("Content-Type: application/octetstream; name=$FILEname"); //for IE & Opera
header("Content-Type: application/octet-stream; name=$FILEname"); //for the rest
header("Content-Disposition: attachment; filename=$FILEname;");
header("Content-Transfer-Encoding: binary");
header("Cache-Control: cache, must-revalidate");
header("Pragma: public");
header("Expires: 0");


Luci不過就是改呼叫luci.http.header()。
[Luci]下載檔案時無法直接開啟文件的解法方法
luci.http.header("Content-Type", "application/octetstream; name=" .. fname); //for IE & Opera
luci.http.header("Content-Type", "application/octet-stream; name=" .. fname); //for the rest
luci.http.header("Content-Disposition", "attachment; filename=" .. fname);
luci.http.header("Content-Transfer-Encoding", "binary");
luci.http.header("Cache-Control", "cache, must-revalidate");
luci.http.header("Pragma", "public");
luci.http.header("Expires", "0");




2012年2月26日 星期日

glibc讀書心得 -- Ch1 Introduction


1.1 Getting Started
C語言並沒有內建一些常用的操作,如input/output、memory management等等,這些通通被定義在Standard Library中(glibc)。

1.2 Standards and Portability
glibc參考的C Library標準有ISO C(American National Standard X3.159-1989—“ANSI C” and later by the International Standardization Organization (ISO): ISO/IEC 9899:1990, “Programming languages—C”.)和POSIX兩個。以及參考System V和Berkeley UNIX兩個實做而成。

1.3 Using the Library
Library在C主要由兩個部份組成:
  1. header files that define types and macros and declare variables and functions
  2. the actual library or archive that contains the definitions of the variables and functions.
為了能使用glibc,你必須將所需的header file,用#include這個preprocessor directive將其引入,嚴格來說,您可以不必include這些header file,只要您可以正確的宣告相關的變數、巨集等等,不過為了效率和能正確的宣告相關變數,我們都會採用header file的形式,將其引入(include)。至於Macro的部份就參考我之前的C preprocessor的文章GCC - C Preprocessor應該會更清楚。
關於Reserved Names個人覺得還蠻重要的,就直接貼出來了:
  1. Names beginning with a capital ‘E’ followed a digit or uppercase letter may be used for additional error code names. See Chapter 2 [Error Reporting], page 13.
  2. Names that begin with either ‘is’ or ‘to’ followed by a lowercase letter may be used for additional character testing and conversion functions. See Chapter 4 [Character Handling], page 65.
  3. Names that begin with ‘LC_’ followed by an uppercase letter may be used for additional macros specifying locale attributes. See Chapter 7 [Locales and Internationalization], page 150.
  4. Names of all existing mathematics functions (see Chapter 19 [Mathematics], page 466) suffixed with ‘f’ or ‘l’ are reserved for corresponding functions that operate on float and long double arguments, respectively.
  5. Names that begin with ‘SIG’ followed by an uppercase letter are reserved for additional signal names. See Section 24.2 [Standard Signals], page 592.
  6. Names that begin with ‘SIG_’ followed by an uppercase letter are reserved for additional signal actions. See Section 24.3.1 [Basic Signal Handling], page 600.
  7. Names beginning with ‘str’, ‘mem’, or ‘wcs’ followed by a lowercase letter are reserved for additional string and array functions. See Chapter 5 [String and Array Utilities], page 73.
  8. Names that end with ‘_t’ are reserved for additional type names.


    參考資料:
  1. The GNU C Library, Chapter 1




2012年1月29日 星期日

JSON-C


JSON-C是一套用C寫的JSON format的parser和generator,可以將JSON字串正確parse,並且存成json_object進行操作(新增/刪除/修改),也可以將json_object轉成JSON字串,個人還蠻推薦的。

官方網站:JSON-C

安裝步驟
brook@vista:~/src$ git clone https://github.com/json-c/json-c.git json-c
Cloning into json-c...
remote: Counting objects: 448, done.
remote: Compressing objects: 100% (169/169), done.
remote: Total 448 (delta 315), reused 405 (delta 274)
Receiving objects: 100% (448/448), 125.68 KiB | 76 KiB/s, done.
Resolving deltas: 100% (315/315), done.
brook@vista:~/src$ cd json-c/
brook@vista:~/src/json-c$ ./autogen.sh
autoreconf: Entering directory `.'
autoreconf: configure.in: not using Gettext
autoreconf: running: aclocal 
autoreconf: configure.in: tracing
autoreconf: running: libtoolize --install --copy
libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.in and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.
autoreconf: running: /usr/bin/autoconf
autoreconf: running: /usr/bin/autoheader
autoreconf: running: automake --add-missing --copy --no-force
autoreconf: Leaving directory `.'
brook@vista:~/src/json-c$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
...
brook@vista:~/src/json-c$ make
make  all-am
make[1]: Entering directory `/home/brook/src/json-c'
/bin/bash ./libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I.    -Wall -Wwrite-strings -Werror -std=gnu99 -D_GNU_SOURCE -D_REENTRANT -g -O2 -MT arraylist.lo -MD -MP -MF .deps/arraylist.Tpo -c -o arraylist.lo arraylist.c
...
brook@vista:~/src/json-c$ make check
make  test1 test2 test4 test_parse_int64 test_null test_cast
make[1]: Entering directory `/home/brook/src/json-c'
gcc -DHAVE_CONFIG_H -I.    -Wall -Wwrite-strings -Werror -std=gnu99 -D_GNU_SOURCE -D_REENTRANT -g -O2 -MT test1.o -MD -MP -MF .deps/test1.Tpo -c -o test1.o test1.c
test1.c: In function ‘sort_fn’:
test1.c:14:8: error: assignment discards ‘const’ qualifier from pointer target type [-Werror]
test1.c:15:8: error: assignment discards ‘const’ qualifier from pointer target type [-Werror]
cc1: all warnings being treated as errors

make[1]: *** [test1.o] Error 1
make[1]: Leaving directory `/home/brook/src/json-c'
make: *** [check-am] Error 2
brook@vista:~/src/json-c$ sed -i 's/-Werror //' Makefile
brook@vista:~/src/json-c$ make check
make  test1 test2 test4 test_parse_int64 test_null test_cast
make[1]: Entering directory `/home/brook/src/json-c'
...
brook@vista:~/src/json-c$ ./test1
my_string= 
my_string.to_string()="\t"
my_string=\
my_string.to_string()="\\"
my_string=foo
my_string.to_string()="foo"
my_int=9
my_int.to_string()=9
my_array=
 [0]=1
 [1]=2
 [2]=3
 [3]=null
 [4]=5
...


Parser
可以利用json_tokener_parse()直接將字串轉成json_object,或是利用json_object_from_file()直接將檔案轉成json_object。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#include "json.h"


int main(int argc, char **argv)
{
  json_object *new_obj;

  MC_SET_DEBUG(1);

  new_obj = json_tokener_parse("/* more difficult test case */ { \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }");
  printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
  json_object_put(new_obj);

  return 0;
}


Generator基本上就是用json_object_to_json_string()將json_object轉成字串。

更多的範例可以參考source code裡面的test1.c。



2011年12月18日 星期日

Turn your Linux computer into a wireless access point using hostapd


話說手持無線裝置越來越多,習慣晚上用我的android手機上網,不過有天竟然把AP帶回老家忘記帶回來,只好拿我的HP Compaq CQ45來充當AP了,網路上已經很多Turn your Linux computer into a wireless access point using hostapd這類的文章了,我就不多加闡述,只是單純的紀錄。
我的CQ45的網卡是Broadcom的Chip,用的Linux kernel是3.0.0,預設的wireless driver是去load wl.ko,產生的interface name是eth1,不過hostap好像不支援,也沒去深究他,索性把他換成舊的b43.ko,然後簡單設定一下hostapd.conf就執行hostapd了。

hostapd.conf
ssid=test
hw_mode=g
channel=1
interface=wlan1
#bridge=br1
driver=nl80211
ignore_broadcast_ssid=0
macaddr_acl=0
wmm_enabled=0

接著還要設定wireless的IP,不過我並沒有啟動DHCP server,Client端就用靜態IP吧。

接著還要讓我的NB具有route和NAT的功能,請執行
sudo iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE
sudo sysctl -w sys.net.ipv4.conf.all.forwarding=1




2011年12月10日 星期六

send signal to user-space


某天有個需求是希望當kernel發生某事件時通知user-space的process,心裡想最快就是送signal,於是google一下,果然有人有類似的需求,signals handling in the kernel,於是改了一下把他放上來,值得一提的是,其實這樣並不被鼓勵的,而且原本的kill_proc_info並沒有被export出來,所以如果是module要使用的話,就必須把他export出來,EXPORT_SYMBOL(kill_proc_info)

#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>

#include <linux/sched.h>
#include <linux/kernel.h> /* printk() */
// #include <linux/slab.h> /* kmalloc() */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/signal.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>

#define PROC_NAME "sig2pid"

/**
 * 送signal 到pid去
 */
static int send_sig_to_pid(int sig, pid_t pid)
{
    struct siginfo info;

    info.si_signo = sig;
    info.si_errno = 0;
    info.si_code = SI_USER; // sent by kill, sigsend, raise
    info.si_pid = get_current()->pid; // sender's pid
    info.si_uid = current_uid(); // sender's uid

    return kill_proc_info(sig, &info, pid);
}

/**
 * /proc/sig2pid的write ops
 */
static int
sig2pid_proc_write(struct file *file, const char __user * buffer,
                     unsigned long count, void *data)
{
    int sig, pid, ret;
    char line[count];
    ret = copy_from_user(line, buffer, count);

    if (ret) {
        return -EFAULT;
    }
    sscanf(line, "%d %d", &pid, &sig);
    printk("%s(#%d): pid(%d), sig(%d)\n",
            __func__, __LINE__, pid, sig);
    send_sig_to_pid(sig, (pid_t) pid);
    return count;
}

/**
 * 建立/proc/sig2pid
 */
static int create_proc_file(void)
{
    struct proc_dir_entry *p;
    p = create_proc_entry(PROC_NAME, S_IFREG | S_IWUGO, NULL);
    if (!p) {
        printk("%s(#%d): create proc entry failed\n", __func__, __LINE__);
        return -EFAULT;
    }
    p->write_proc = sig2pid_proc_write;
    return 0;
}

int sig2pid_init_module(void)
{
    return create_proc_file();
}

void sig2pid_exit_module(void)
{
    remove_proc_entry(PROC_NAME, NULL);
}

module_init(sig2pid_init_module);
module_exit(sig2pid_exit_module);


    參考資料:
  • http://old.nabble.com/signals-handling-in-the-kernel-to12032525.html#a12032525 , signals handling in the kernel.
  • http://kerneltrap.org/node/5800, how to send signal from kernel space to user space.

github: https://github.com/brook-kuo/Linux_Module/tree/master/process/send_sig_to_userspace


2011年11月27日 星期日

Allow Unix sockets to be treated like normal files.


某天忽然想要用echo/cat的方式直接對unix socket做存取,結果得到error,只好有請google大神,覓得此良方Allow Unix sockets to be treated like normal files,try了一下沒問題。



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
    int srv_fd, cli_fd;
    socklen_t cli_len;
    struct sockaddr_un srv_addr, cli_addr;
    char buf[128] = "Brook: ";
    ssize_t len;

    unlink("server_socket");
    if ((srv_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        handle_error("socket");
    }

    srv_addr.sun_family = AF_UNIX;
    strcpy(srv_addr.sun_path, "/tmp/unix_sock");
    if (bind(srv_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) {
        handle_error("bind");
    }

    if (listen(srv_fd, 1) < 0) {
         handle_error("listen");
    }

    while (1) {
        cli_fd = accept(srv_fd, (struct sockaddr *)&cli_addr, &cli_len);
        len = read(cli_fd, buf + 6, sizeof(buf) - 6);
        buf[6 + len] = 0;
        write(cli_fd, buf, strlen(buf));
        close(cli_fd);
    }
    return 0;
}

    參考資料:
  • http://lwn.net/Articles/415651/ , net/unix: Allow Unix sockets to be treated like normal files.


github:
https://github.com/brook-kuo/Linux_Module/tree/master/socket/unix_as_normal_file


2011年11月19日 星期六

socket programming in bash


關於Bash的Socket部份您可以在man page中看到這段描述:
/dev/tcp/host/port
        If host is a valid hostname or Internet address, and port is an integer
        port number or service name, bash attempts to open a TCP connection to
        the corresponding socket.

/dev/udp/host/port
        If host is a valid hostname or Internet address, and port is an integer
        port number or service name, bash attempts to open a UDP connection to 
        the corresponding socket.

用一個簡單的例子就會懂了



2011年11月13日 星期日

HTML5 canvas的初體驗


HTML5的新的element,canvas,可以在Browser上繪製圖表/圖片。使用時必須訂出繪製的範圍(width/height),接著就可以開始用JavaScript進行繪圖了。目前多數支援canvas的browser看來也都只有支援2D,未來應該會有3D的。
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Canvas/Simple shapes (rectangles)</title>
    </head>
<body>
    <canvas id="myCanvas" width="300" height="150">
    Fallback content, in case the browser does not support Canvas.
    </canvas>
    <script type="application/x-javascript">
        // Get a reference to the element.
        var elem = document.getElementById('myCanvas');

        // 判斷是否能取得context
        if (elem && elem.getContext) {
            // 你只能對每一個canvas做initialize一次(getContext).
            // context = canvas . getContext(contextId [, ... ])
            var context = elem.getContext('2d');
            if (context) {
                // context.fillRect(x, y, w, h)
                // 畫方形
                context.fillRect(0, 0, 150, 100);

                // context.clearRect(x, y, w, h)
                // 清方形
                context.clearRect(100,50, 50, 50);

                // context.strokeRect(x, y, w, h)
                // 畫框
                context.strokeRect(150,100, 50, 50);
            }
        }
    </script>
</body>
</html>



fillRect(x, y, w, h)使用fillStyle來決定顏色。
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Canvas/FillRect attribute</title>
    </head>
<body>
    <canvas id="myCanvas" width="300" height="150">
    Fallback content, in case the browser does not support Canvas.
    </canvas>
    <script type="application/x-javascript">
        // Get a reference to the element.
        var elem = document.getElementById('myCanvas');

        // 判斷是否能取得context
        if (elem && elem.getContext) {
            // 你只能對每一個canvas做initialize一次(getContext).
            // context = canvas . getContext(contextId [, ... ])
            var context = elem.getContext('2d');
            if (context) {
                context.fillStyle = 'pink';
                context.fillRect(0, 0, 50, 50);

                context.fillStyle = '#f00'; // red
                context.fillRect(50, 50, 50, 50);
                context.fillStyle = '#0f0'; // green
                context.fillRect(100, 100, 50, 50);
                context.fillStyle = '#00f'; // blue
                context.fillRect(150, 150, 50, 50);

                // RGBA(red, green, blue, alpha)
                // Alpha1是透明度
                context.fillStyle = 'RGBA(100, 100, 255, 0.2)';
                context.fillRect(50, 50, 150, 150);
            }
        }
    </script>
</body>
</html>



而strokeRect(x, y, w, h)則會使用strokeStyle(顏色), lineWidth(粗細), lineJoin(連接觸樣式),等屬性來決定,其實就是line style。
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Canvas/Line styles</title>
    </head>
<body>
    <canvas id="myCanvas" width="300" height="150">
    Fallback content, in case the browser does not support Canvas.
    </canvas>
    <script type="application/x-javascript">
        // Get a reference to the element.
        var elem = document.getElementById('myCanvas');

        // 判斷是否能取得context
        if (elem && elem.getContext) {
            // 你只能對每一個canvas做initialize一次(getContext).
            // context = canvas . getContext(contextId [, ... ])
            var context = elem.getContext('2d');
            if (context) {
                // The beginPath() starts a new path
                context.beginPath();
                // reset the path to (0, 0)
                context.moveTo(0,0);
                context.strokeStyle = '#f00';

                // lineWidth是線的大小
                context.lineWidth = 1;
                context.strokeRect(0, 0, 20, 20);
                context.lineWidth = 10;
                context.strokeRect(30, 30, 20, 20);

                // LineJoin是連接處(轉角)的樣式
                context.lineJoin = 'bevel';
                context.strokeRect(60, 60, 20, 20);

                context.lineJoin = 'round';
                context.strokeRect(90, 90, 20, 20);

                context.lineJoin = 'miter';
                context.strokeRect(120, 120, 20, 20);
            }
        }
    </script>
</body>
</html>



參考資料:
  1. http://dev.opera.com/articles/view/html-5-canvas-the-basics/
  2. http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html
  3. https://developer.mozilla.org/en/Canvas_tutorial/Using_images
  4. http://wiki.moztw.org/%E7%94%A8_Canvas_%E7%95%AB%E5%9C%96
  5. http://www.w3school.com.cn/htmldom/dom_obj_canvas.asp


source code:
https://github.com/brook-kuo/JavaScript/tree/master/html5/canvas



2011年11月6日 星期日

關於bridge中hard_header_len/needed_headroom的問題


我的interface在收送上本來沒有問題,但是當它和其他interface作bridge之後就發生crash了,後來才發現是head不夠大,於是參考這篇文章"关于bridge中hard_header_len值的问题",做了些修改,原因是因為我的device在預留head room時,是hard_header_len+needed_headroom。而且有些local generate的packet會使用LL_RESERVED_SPACE()這macro來保留head room,而LL_RESERVED_SPACE()也會使用到needed_headroom,所以最保險的作法就是同時設定bridge的hard_header_len和needed_headroom。
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -369,6 +369,10 @@ void br_features_recompute(struct net_bridge *br)
 {
        struct net_bridge_port *p;
        unsigned long features, mask;
+#if 1 // Brook.
+    unsigned short max_hard_header_len = ETH_HLEN;
+    unsigned short max_needed_headroom = 0;
+#endif

        features = mask = br->feature_mask;
        if (list_empty(&br->port_list))
@@ -379,8 +383,22 @@ void br_features_recompute(struct net_bridge *br)
        list_for_each_entry(p, &br->port_list, list) {
                features = netdev_increment_features(features,
                                                     p->dev->features, mask);
+#if 1 // Brook.
+        if (p->dev->hard_header_len > max_hard_header_len) {
+            max_hard_header_len = p->dev->hard_header_len;
+        }
+
+        if (p->dev->needed_headroom > max_needed_headroom) {
+            max_needed_headroom = p->dev->needed_headroom;
+        }
+#endif
        }

+#if 1 // Brook.
+    br->dev->hard_header_len = max_hard_header_len;
+    br->dev->needed_headroom = max_needed_headroom;
+#endif
+
 done:
        br->dev->features = netdev_fix_features(features, NULL);
 }

    參考資料:
  1. 关于bridge中hard_header_len值的问题
  2. struct net_device结构中添加了needed_headroom/needed_tailroom成员



利用gen_init_cpio建立initrd的script


利用gen_init_cpio建立initrd的script
#!/bin/bash
#kernel的目錄
KERN_DIR=/usr/src/linux-kvm
#gen_initramfs_list.sh產生的暫存檔
INITRAMFS_LIST=/tmp/gen_initramfs_list
#initramfs的來源目錄, 為傳入該script的第一個參數
INITRAMFS_DIR=$1
#initrd的目的檔名, 為傳入該script的第二個參數
INITRD=$2
#給INITRAMFS_DIR default值
: ${INITRAMFS_DIR:="/home/brook/projects/rootfs"}
#給INITRD default值, 
: ${INITRD:="/home/brook/initrd"}

if [ ! -d $INITRAMFS_DIR ]; then
    echo "usage: $0 <initramfs_dir> <output_file>"
    exit 1
fi

sh $KERN_DIR/scripts/gen_initramfs_list.sh -d $INITRAMFS_DIR > $INITRAMFS_LIST
$KERN_DIR/usr/gen_init_cpio $INITRAMFS_LIST > $INITRD



2011年10月22日 星期六

javascript(6.1) - event, onkerpress


onkeypress當鍵盤被按下時觸發,這個event算蠻常用的,尤其是想要限制使用者的輸入範圍時。 其範例如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>   <title></title> </head> <body>   <script type="text/javascript">     function noNumbers(e)     {        var keynum        var keychar        var numcheck        if (window.event) { // IE          keynum = window.event.keyCode        } else if(e.which) { // Netscape/Firefox/Opera          keynum = e.which        }        alert(keynum)        keychar = String.fromCharCode(keynum)        numcheck = /\d/        return !numcheck.test(keychar)     }   </script>   <form>     <input type="text" onkeypress="return noNumbers(event)">     <input type="text" id="k">   </form>   <script type="text/javascript">     document.getElementById("k").onkeypress = noNumbers;   </script> </body> </html>
這邊要注意的是,不同的瀏覽器會由不同的方式取得該event的值。


參考資料:W3schools, onkeypress Event


2011年9月29日 星期四

[讀書心得]大債時代(序)


「如果你欠銀行一百萬,你的命運掌握在銀行手中;如果你欠銀行一百億,銀行的命運掌握在你手中。」這一句開場白讓我印象深刻,台灣的茂德就是一個例子了,以股作債,金融遊戲沒有所謂的死巷,只有怎麼轉,讓錢轉進你的口袋。
「如果這個世界上,人人都擁有大量存款,沒有債務,那只會導致兩件事發生:第一,銀行倒閉,第二,存款變廢紙,擁有糧食的,才是真正富人。」第一件事情會不會發生我不確定,但是第二件事情肯定會發生,其實這世界最殘酷的事實就是不可能均富,一定要有人擁有大筆財富,而另一大群人缺錢,為錢賣命工作,這世界才可能進步,鄧小平說的最坦白了,先讓一部分的人富起來,沒有說要讓大家都富起來。
序的後面還有一段話讓我感受頗深,「我們希望自己的收入不斷成長,不斷的投入更多的時間與精力,追求下一份更高的薪水、更好的享受。快樂永遠是下一次加薪,下一次旅行,下一次購物。我們從不滿足於自己已經所有的。想像一下,有一個人在四、五十歲的年紀,有一天突然心臟病發作。雖然事後幸運地復原了,但發作那一霎那心痛如刀剮,讓他以為自己就要離開人世的瞬間,卻讓他看清了人生的目的。從此以後,他過著更健康的生活,更珍惜與家人與朋友相處的時光,更知道拿一些時間來做自己覺得真正重要的事。一次健康上的小打擊,卻讓他的人生之路走得更快樂踏實。」如果說,賺錢是要讓自己和家人過得更好,那麼在你追求財富的路途上,是不是真的有過得比較好,還是只看到帳面上的數字在增加,自己的身體或心理卻反其道而行,家人之間的感情是否更好,還是越來越疏遠了,我想都是值得我們去省思的。



2011年9月18日 星期日

[Lua Note] 2 - Types and Values


Types and Values

Lua的變數型態完全取決於assign的值,而Lua有8個基本資料型態nil、boolean、number、string、userdata、function、thread和 table等。
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> print(type(a))
nil
> a="hello"
> print(type(a))
string
> a=3*5
> print(type(a))
number
> a=function() end
> print(type(a))
function
> a=true
> print(type(a))
boolean


nil

nil和C/Java Script的null意思差不多,當宣告一個變數還沒有給值,預設值就是nil,而當給變數nil就相當於delete這個變數。
> x = {}
> x["y"] = 1
> for k,v in pairs(x) do
>> print(k,v)
>> end
y       1
> x["y"] = nil -- 將y自x中移除
> for k,v in pairs(x) do
>> print(k,v)
>> end
> -- 沒有印出y了


booleans

說到boolean大家應該都知道boolean只有兩種值,true/false,在lua中,除了nil和false是false以外,其他都是true。not則是反轉boolean。
> print(x)
nil
> print(not not x)
false
> x=0
> print(not not x)
true -- 和C不同唷
> x=false
> print(not not x)
false
> x = function() end
> print(not not x)
true


numbers

Lua中沒有整數(integer),數值(numbers)是用雙精度儲存(double-precision floating-point)。


strings

Lua中的字串是儲存8-bit的字元,所以您可以儲存任何的資訊,和C不同的地方是C用null-terminated("\0"當結數符號),而Lua會用length紀錄字串長度。
當Lua中的字串是不能改變的,一旦改變就等同重新建立一個新字串,這點和C也不同。
Lua的字串除了可以用雙引號和單引號表示外,還可以用 [[...]]表示。
> s1="test1"
> s2='test2'
> s3=[[
>> test1
>> test2
>> ]]
> print(type(s3))
string
> print(s3)
test1
test2

Lua中字串可以用..串接在一起。
和JavaScript一樣,Lua在字串和數字之間的數學運算會試著將字串轉成數字,轉失敗就會出現error。
> print(10 .. "1")
101
> print(10 + "1")
11
> print(10 .. "x")
10x
> print(10 + "x")
stdin:1: attempt to perform arithmetic on a string value
stack traceback:
        stdin:1: in main chunk
        [C]: ?


table

table也稱為associative arrays,key可以是任何的資料型態,除了nil以外,都可以當key(或稱index),而任何其值可以是任何的資料型態,其表示法和一般C的array類似都是用"[]"來取值。
> x = {
[1] = "one",
["2"] = "two",
[3.0] = "three",
}
> print(x[1])
one
> print(x["2"]); print(x[3.0])
two
three



2011年9月17日 星期六

[Lua Note] 1 - Getting Started


Chunks

Lua就和一般的Script一樣,沒有所謂的main function,也不需要分號";"'當成一個statement的結束,分號可有可無,不過我還是習慣性的加上去,就像數學運算式的括號,很可能你想的和你寫出來的意思不同,導致錯誤,以下的範例都是一樣的意思
 
    a = 1
    b = a*2
    
    a = 1;
    b = a*2;
    
    a = 1 ; b = a*2
    
    -- "--"是註解的意思
    a = 1   b = a*2    -- 很醜,但還是合法

您可以在提示符號下執行lua,lua [ options ] [ script [ args ] ]
prompt> lua 會進入interactive 模式。
prompt> lua -la -lb會執行a.lua和b.lua,-l相當於require的意思,lua就像一般的computer language一樣,允許您寫自己的library,並且透過require載入。
lua也提供另外dofile這個function,讓您測試您寫的lua檔,dofile和require最大的不同是,require只會載入一次,而dofile可以重覆載入,所以拿來驗證lua檔就非常適合了。



Global Variables

在Lua中,沒有特別宣告local就是Global Variables,用到沒有宣告的變數,其值就是nil
 
function scope1()      -- step 4.
    function scope2()  -- step. 8
        print(A)               -- step. 9 印出A=20
        local A=30         -- step. 10又宣告一個變數A(範圍更小)
        print(A)               -- step. 11 印出該範圍的A, 即30
    end
    print(A)           -- step 5. 印出 10
    local A=20     -- step 6. 新的A=20, Global的A一樣不變
    scope2()        -- step 7.
     print(A)          -- step 12. 印出這個範圍的A = 20
end

print(A)                -- step 1. 印出 nil
A=10                   -- step 2. A=10
scope1()             -- step 3.
print(A)                -- step 13. 印出這個範圍的A = 10
os.exit()



Some Lexical Conventions

和一般的language一樣,會有所謂的key word,以下是lua的key word
while
and break do else elseif
end false for function if
in local nil not or
repeatreturn then true until

lua是case-sensitive就是有分大小寫的。 變數x和變數X是不同的。
在Lua中的註解 --是單行,像C中的//,而--[[ --]]就像C的/* */屬於多行註解。好用的地方是,只要在"--[["前面多一個"-"就等於取消整個註解,超好用。
 
--[[
    print(10)         -- no action (comment)
--]]

---[[
    print(10)         --> 10
--]]


The Stand-Alone Interpreter

lua會將command-line的參數存到陣列arg中,arg[0]存放執行的script,arg[-1]存放script檔名的前一個參數,arg[1]..arg[n]就是依序存放後面的參數。


參考資料:Programming in Lua (first edition)




2011年9月12日 星期一

Lua - Introduction


最近有幸開發新的Web Page,用的是LuCI(Lua Unified Configuration Interface),所以就得先學學Lua嚕,在這裡做些筆記,也和大家分享一下。
Lua是一種embedded language,就像Java Script一樣,通常會依附在host program底下(就像IE和Java script的關係一樣),而host program會執行Lua的程式碼,(就像網頁中參雜Java Script一樣)。不過,在寫LuCI的時候,感覺比較像在寫PHP或者ASP之類的。

Lua主用是用C寫的,原始的Lua非常的小,因為只有提供非常精簡的能力,但是可以透過擴充Library變得更強(更肥),因為精簡,所以適合在Embedded System上使用。

www.lua.org是Lua的官方網站,其中最重要的莫過於裡面的免費書籍,Programming in Lua (first edition),這本是必看的,後面的筆記應該都會是源自這邊,熟悉Lua之後,也可以看看Lua 5.1 Reference Manual
該網站還提供Web版的Lua,讓您沒有Lua也能學Lua。


如果您是在Windows環境底下,您可以安裝luaforwindows來練習,Practice Makes Perfect。


2011年8月6日 星期六

core dump and debug



看ulimit預設的一些參數,注意core file size若是0,就不會產生core文件了。

brook@vista:~$ cat -n foo.c 
     1  #include 
     2
     3  static void sub(void);
     4
     5  int main(void)
     6  {
     7      sub();
     8      return 0;
     9  }
    10
    11  static void sub(void)
    12  {
    13      int *p = NULL;
    14
    15      /* derefernce a null pointer, expect core dump. */
    16      printf("%d", *p);
    17  }
    18
    19
brook@vista:~$ gcc -g -Wall foo.c 
brook@vista:~$ ./a.out 
Segmentation fault
brook@vista:~$ ls core*
ls: cannot access core*: No such file or directory
brook@vista:~$ ulimit -c 1024
brook@vista:~$ ./a.out 
Segmentation fault (core dumped)
brook@vista:~$ ls core*
core
brook@vista:~$ gdb ./a.out core 
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
 Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
...
Reading symbols from /home/brook/a.out...done.
[New Thread 5382]

warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.13.so...done.
done.
Loaded symbols for /lib/x86_64-linux-gnu/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000400518 in sub () at foo.c:16
16          printf("%d", *p);
(gdb) 


    參考資料:
  1. http://blog.csdn.net/borefo/article/details/5029555, 在Linux下产生并调试core文件



2011年7月31日 星期日

台中 - 韓式料理 - 韓香 ☆☆☆


韓香位於居仁國中後面,就在中華電信旁,地理位置應該不難找,因為生意還算不錯,所以如果要吃,記得先預約唷,不然就得在外面慢慢排了,看到很多椅子了吧?就知道要等很久。


這是這家店的菜單,不過每次去都還是必點銅板烤肉+石鍋拌飯。




這家店最大的特色就是,小菜無限量供應,這邊的小菜大概有十多種吧,每次去每個人至少都可以吃掉10盤。



這邊有低消的限制唷。






地點:台中市市府路39號
價位:最低消費140元,另加收10%服務費



熱門文章