2015年4月27日 星期一

fopen的開檔,讀檔與寫檔

開檔,讀檔,寫檔在資訊科學這門課程是相當基礎的資料處理,

但是假使平常比較不常做這些處理,當要你作的時候,真的是很要人命

因此作一些筆記是必要的,到時候要處理檔案的時候,就可以很快上手了

這邊分三部分來作筆記,分別為讀檔,寫檔,讀檔並寫檔

===========================================================
首先是讀檔的部分:

讀檔和寫檔是較為簡單的部分,讀檔的話基本上第一步先對檔案作fopen

接著只要使用fgets,這支C函式可以幫你把檔案一行一行的讀出來,fgets也會在每一行讀到結
尾"\n"時幫你把檔案指標指到下一行的起始

簡單的範例如下:
///////////// Read_File.c////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define PATH "/etc/login.info"

int main()
{
    FILE *fptr;
    char login_info[128];

    if ((fptr = fopen(PATH, "r")) == NULL)
    {
                printf("open_file_error");
                exit(1);
     }
   
     while(fgets(login_info, 128, fptr) != NULL)
     {
                //fgets會把每一行資訊copy到login_info字元陣列
                //此處油開發者自行決定要對讀出的內容作甚麼處理
                //寫檔則再搭配使用 fwrite(buffer,1,sizeof(buffer),fptr);
      }
      fclose(fptr);
      return 1;
}
///////////////////////////////////////////////////////////////////////

fopen的第二個參數,用法請參考下圖:




接著是讀檔並且同時寫檔,作者這邊就只作一些重點整理了

首先也是對檔案作fopen,後面參數就請填上"r+"

接著以下這幾個function的使用就是讀檔並同時寫檔的重點

**********************************************
int ftell(FILE *fptr);
==>這支function可以取到目前fptr所offset的位置

舉個例子有個login.info的檔案內容如下:
username:admin
password:1234

那我用fptr=fopen("login.info", "r+")之後再搭配fget一次之後去作ftell(fptr)會等於15
也就是此時指標的位置會是在第二行一開頭
******************************************************

******************************************************
第二個重要的是
rewind(FILE *fptr);
==>這支function可以把fptr指標移到最前頭
*******************************************************

**************************************************
最後一個是
fseek(FILE *fptr, int offset, SEEK_SET);
==>此fuction可以幫你把fptr的指標位置移到你想要開始的地方

接著你可以作fprintf(fptr, " "); 你就可以修改你想改寫的部分了
******************************************************


2015年4月21日 星期二

C語言cgi搭配ajax的傳遞,搭配的Server是thttpd

今天要弄一個登入的功能

作法是html去作js檔裡面的Login函式

有問題的地方是在作Login函式時,會再透過ajax去送data給cgi作id與passwd的判斷

javascript的ajax程式碼如下所示:(擷取部分)

/////////////////////////////////////////////////////////////////////////////////
var url_Cgi="./cgi-bin/login.cgi";

var TransData = String.format("username={0}&password={1}",
UserName, Password);

$.ajax({
        url: url_Cgi,
        type: 'GET',
        async: false,
        data: TransData,
        error: function()
{
            AlertNetworkError();  
        },
        success: function(result)
{
if(result == login_success)
{
location = "main.html";
}
else
{
ShowAlert(result) );
}
        }

送過去的data可以知道是ajax的data部分,ajax會呼叫cgi程式,並把要送到cgi的字串帶到login.cgi的後面(例如login.cgi?username=danny&password=0000)

今天遇到的問題就是cgi怎麼把值回傳給ajax的result

原本以為cgi程式那邊的return值會傳給ajax的result,原來我的想法是錯的

cgi如果要想傳值給ajax的result,其實只需在程式裡面作下面的程式碼:

printf("Content-type:text/html\n\n");
printf("login_success");

這樣子作,ajax那邊拿到的result值就會是"login_success"了!


2015年4月17日 星期五

用C寫cgi時 popen或system遇到取值是空的問題

今天在寫cgi網頁時,用了前幾天學到的popen取設備端ip值的方法,如下:

char MyIpBuf[ MY_IP_LENTH ]={0};
FILE *fpRead;

//使用shell command來取得ip值  
char* command=(char*)"ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";
char* renewCh;
 
fpRead = popen(command, "r");
fgets(MyIpBuf, MY_IP_LENTH , fpRead);


奇怪的是,在browser輸入這個cgi網址時,怎麼popen取到的MyIpBuf值是空的,


這個問題讓我找了好久,也嘗試用system先輸出到檔案的方式,值還是空

奇怪的是,如果編譯成可執行檔,直接在terminal執行都取的到我要的值,

但是只要是編譯成cgi透過Browser執行,就會是空


後來發現把ifconfig改成完整的路徑,如下面這樣就沒有問題了:

char* command=(char*)"/sbin/ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";

猜測應該是Browser執行shell時,PATH路徑沒有/sbin因此找不到ifconfig這個指令吧?!

2015年4月15日 星期三

libstdc++ 太過龐大的瘦身方法 arm-linux-strip

最近在porting live555 到我們arm的板子上

由於板子只有16MB的空間可供使用(uboot+kernel+squashfs+user_space),可以說空間相當的吃緊

偏偏在作live555 cross-compiler放到板子後,竟然缺少了libstdc++.so.6這個C++的library。



缺了就補給他,於是跑去toolchain中找這個for arm的C++ libray,發現檔案大小竟然是3.7MB


若是這個檔案放上板子,空間就被去掉1/4了 @@"

於是就找了一些瘦身的方法,找到一個不錯的指令 "arm-linux-strip"

其作用是: 去掉elf可執行文件的資訊,使可執行文件變小


其實也是標準編譯時就可以加入的指令,使用之後發現還真不錯用 3.7MB變成----> 600KB左右

真是解決了我的心頭大患,不過如果編譯過程已經有加入這指令,你再執行一次大小是不會改變的

另外有看到一個滿厲害的東西 "用C++寫code 不用用到C++ lib去編譯" 可以參考這篇作者寫的

http://ptspts.blogspot.tw/2010/12/how-to-write-c-program-without-libstdc.html


不過要這樣做,在寫code的過程中,會有一些header檔不能include的限制~

原來busybox --install 的意思是這樣

今天學習了 busybox --install . 這指令的意義

第二個參數--install,busybox會把自己所有的指令集以hard link的方式安裝在當下路徑當中


另外一個指令是 busybox --install -s .

則是代表busybox會把自己所有的指令集以soft link的方式安裝在當下路徑當中

linux的環境下,在C程式裡popen的用法&取得ip位置的範例

記得以前有寫過network setting的頁面,其中必須取得現在環境中的IP值。

記得還是菜逼八的時候,是用system call去呼叫事先寫好的script,這支script作了ifconfig之後搭配sed或是awk篩選出 IP值之後再用 echo 輸出到某個檔案。

而最後要取IP值時,再想辦法從檔案中拿出來...




其實根本不用作的這麼複雜,使用popen()來作,還可以直接把取到的值放入變數中。
有關popen的功能以及原型,如下所示:

FILE *popen(const char *command, const char *type);

簡單的說就是popen會把command這個字串使用shell的方式去執行,接著輸出到C的程序裡。




下面的範例,就是一個可以取得IP位址的範例:
//============== get_ip.c ================
#include <stdio.h>
#include <string.h>

#define MY_IP_LENTH 32

int main()
{
     char MyIpBuf[ MY_IP_LENTH ]={0};
     FILE *fpRead;

     //使用shell command來取得ip值   
    char* command=(char*)"ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";
    char* renewCh;

    fpRead = popen(command, "r");
    fgets(MyIpBuf, MY_IP_LENTH , fpRead);

    //記得作pclose()的動作
    if(fpRead != NULL)
pclose(fpRead);
 
    //最後檢查取出的字串當中是否有多餘的換行,若有直接取代為'\0'作結尾
    renewCh=strstr(MyIpBuf,"\n");
    if(renewCh)
*renewCh= '\0';

    printf("=== %s ===\n", MyIpBuf);

    return 0;
}


輸出結果:


2015年4月3日 星期五

Linux環境下,thttpd支援cgi遇到的問題

架設好thttpd Server之後,在測試cgi是否能正常運作時遇到了一點點的問題

在多方詢問與請教估狗大神之後,終於有了頭緒。

首先我實驗的網址是我的本機端 http://localhost/cgi-bin/HelloWorld.cgi

總共會遇到的問題可能有三個,下面將一一描述:


1. 首先來看看thttp.conf這個設定檔
#======= thttp.conf ========
# This section overrides defaults
dir=/home/httpd/html
chroot  
user=root
# default = nobody
.....
.....
.....

要使用cgi第一部分要注意的是上面的紅字的部分,這樣的寫法當你要去執上面的網址時
會遇到下面的狀況:













Solution: 修改thttpd.conf請將chroot註解掉,如下所示:
#======= thttp.conf ========
# This section overrides defaults
dir=/home/httpd/html
#chroot  
user=root
# default = nobody
.....
.....
.....



2.第二個會遇到的問題,當你輸入網址之後可能遇到下面的狀況:













Solution: 遇到這個問題請先去修改thttpd.conf,可能原因是 "cgipat=/cgi-bin/"後面必須加個"*" 

正確的內容如下所示:

#======== thttpd.conf =========
# This section overrides defaults
dir=/home/httpd/html
#chroot
user=root
# default = nobody
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
# This section _documents_ defaults in effect
port=80
# nosymlink# default = !chroot
# novhost
cgipat=/cgi-bin/*        <======後面加個"*"



3. 第三個會遇到的問題是,當輸入網址後Browser會出現下載cgi檔案的提示,而不是顯示出你要的cgi頁面

Solution: 這個問題是因為你cgi檔案設定的權限有問題,似乎是因為cgi檔案必須要有可執行權限,解決方法是只需用以下指令
# chmod 755 HelloWorld.cgi

權限更改成功之後 終於看到cgi要show的畫面了 :D














總結: cgi網頁有問題時

1. 請修改thttpd.conf如下:
#======== thttpd.conf =========
# This section overrides defaults
dir=/home/httpd/html
#chroot
user=root
# default = nobody
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
# This section _documents_ defaults in effect
port=80
# nosymlink# default = !chroot
# novhost
cgipat=/cgi-bin/*   


2.# chmod 755 HelloWorld.cgi


以上~ 




2015年4月1日 星期三

嵌入式網頁遇到 The requested URL '/' resolves to a file which is marked executable but is not a CGI file 的問題

Device上面已經架設好了thttpd,也有順利的執行幾個網頁

今天要連上Device的某個網頁時,Browser上面一直出現錯誤訊息,如下所示:






接著一直著墨在html語法方面,一直尋找是不是有甚麼地方筆誤了

後來在google上找到了解法:

其實只需要把你連的這個網頁改成不可執行,其實就不會有這樣的錯誤訊息了。

所以請更改網頁的權限,指令如下:

# chmod a-x index.html

就不會有問題了!