Announcement

Collapse
No announcement yet.

[OSS-UIT-LUG] Split Join File Program and How to build and install in linux - Draft

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • [Ansi C] [OSS-UIT-LUG] Split Join File Program and How to build and install in linux - Draft

    Nhằm mục đích là PR cho UIT-LUG nên Team sẽ post một số bài hướng dẫn về lập trình trên Linux và một số kiến thức liên quan về Linux hơn nữa đây cũng coi như là nhật ký sổ tay ghi lại những điều đã học tránh bị quên sau khi học xong or nếu quên thì xem lại nhanh nhớ và có thể áp dụng liền.
    Lịch: Team UIT-LUG sẽ sinh hoạt định kỳ vào Sáng thứ 2 hàng tuần bắt đầu từ lúc 8h00 am. Ai đi cũng được cả kéo thêm bạn bè đi càng tốt.

    Về chương trình Split Join File là bài tập làm quen với lập trình trên môi trường Linux thời gian hoàn thành trong vòng 1 tuần và có 1 tuần để build nó đem cài thử vào máy mình và người khác.


    Về yêu cầu và chức năng chính:
    - Chương trình thực hiện bằng command argument như các chương trình khác trên linux.
    Ví dụ với lệnh liệt kê các thư mục trong linux: ls liệt kê tất cả các thư mục và file của thư mục hiện tại ra, ls --help show ra phần hướng dẫn sử dụng, ls -a liệt kê ra tất cả file và thư mục kể cả thư mục và file ẩn.


    Lưu ý: không phải là nhập từng bước như các bạn vẫn làm trên Lập trình C trên Windows
    - split được file
    - merge được file
    - build bằng GNU Autotools và đem cài trên máy người khác được.
    - Viết bằng C or C++ lý do là vì viết cái này thì ai cũng sẽ viết được vì đều đã học tin học đại cương or nói cách khác là không có sinh viên năm nhất ở đây.

    Về các thành phần cần thiết khi lập trình và chạy chương trình.
    - OS: 1 Distro bất kỳ của Linux có thể là Ubuntu (recommend), Fedora, Arch Linux, Linux Mint ....
    - IDE: Eclipse (recommend), NetBeans, Code Blocks ...
    - Compiler : GCC
    - GNU AutoTools
    - Google
    ....


    Về cấu trúc chương trình.
    - Source: gồm có các file sau: split.c split.h merge.c merge.h procedure.c procedure.h main.c
    - Phần cài đặt gồm có:
    aclocal.m4 config.h.in Makefile README
    AUTHORS configure.scan Makefile.am src
    autom4te.cache config.log COPYING stamp-h1
    autoscan.log config.status depcomp Makefile.in
    ChangeLog configure INSTALL missing
    config.h configure.ac install-sh NEWS


    Về phần code:
    - Split File
    Ý tưởng: Để split file đầu tiên ta sẽ đọc file ghi vào buffer tiếp đó ta sẽ lần lượt đọc buffer theo 1 chu kỳ nhất định trong mỗi chu kỳ ta sẽ ghi vào từng file part với kích thước được đã được chia.
    Một số hàm chính cần tìm hiểu:
    ___fread: http://www.cplusplus.com/reference/c.../cstdio/fread/
    ___fwrite: http://www.cplusplus.com/reference/c...cstdio/fwrite/

    Một lưu ý nhỏ ở đây là về kiểu của các biến ở đây ta sẽ dùng kiểu unsigned long để lưu các buffer get từ file vì sao ư đoạn sau sẽ giải thích.

    Quan trọng hơn nữa là ở việc chúng ta mở file ở đây chúng ta phải open file dưới mode là rb (read) or wb (write) or ab (append) để dùng. Vì sao ư? Đoạn sau sẽ giải thích.

    Code:
    int split_by_part(char *filepartName, unsigned int part) {
        FILE *fRead;
        unsigned long fileSize; // file Size
        unsigned char * buffer;
        unsigned long result;
        // Read
        fRead = fopen(filepartName, "rb");
        if (fRead == NULL) {
            fputs("File error", stderr);
            exit(1);
        }
        // get file size
        fileSize = get_file_size(filepartName);
        // allocate memory to contain the whole file:
        buffer = (char*) malloc(sizeof(char) * fileSize);
        if (buffer == NULL) {
            fputs("Memory error", stderr);
            exit(2);
        }
        // create part
        int i = 1;
        unsigned long partSize;
        partSize = fileSize / part;
        while (result = fread(buffer, 1, partSize, fRead) && i <= part) {
            FILE * fWrite;
            char *partName;
            partName = malloc(strlen(filepartName) + 4);
            sprintf(partName, "%s.part%d", filepartName, i);
            // last past
            if (i == part) {
                partSize = fileSize - partSize * (i - 1);
            }
            // write file
            fWrite = fopen(partName, "wb");
            fwrite(buffer, 1, partSize, fWrite);
            fclose(fWrite);
            i++;
            free(partName);
        }
        // terminate
        fclose(fRead);
        free(buffer);
        return 0;
    }
    - Get File Size: có nhiều cách nhưng cách đơn giản nhất là dùng thư viện cho nó đẹp cách khác là dùng fseek
    Ở đây ta dùng thư viện "sys/stat.h" đây là thư viện chuẩn nên bạn không cần phải include từ 1 file nào ngoài cả.
    Code:
    unsigned long get_file_size(char *filename) {
        struct stat st;
        if (stat(filename, &st) == 0)
            return st.st_size;
        fprintf(stderr, "Cannot determine size of: %s\n", filename);
        return -1;
    }
    - Merge file
    Ý tưởng: tương tự như split file ta cũng đọc từng phần của các file part vừa đọc ta vừa ghi vào file merge.
    Ở đây quan trọng là làm sao ta nhận ra file nào là file part nếu chúng ta muốn merge tự động.
    Muốn làm được như thế ta sẽ xài thư viện "glob.h"
    http://pubs.opengroup.org/onlinepubs/007904875/basedefs/glob.h.html để matching các pattern mình định nghĩa.

    Ở đây mình đã làm theo cách là gộp những file có đuôi là name + (.part0x) đi.
    Ví dụ: mình nhập tên file cần join là Forrest Gump.mkv thì nó sẽ từ động tìm những file có tên là Forrest Gump.mkv.part1 , Forrest Gump.mkv.part2 ... để gộp lại thành file Forrest Gump.mkv


    Code:
    int merge_file_by_parttern(char *filename) {
        glob_t globbuf;
        char pattern[255];
        sprintf(pattern, "%s.part*", filename);
    
        switch (glob(pattern, GLOB_DOOFFS, NULL, &globbuf)) {
        case 0:
            break;
        case GLOB_NOSPACE:
            printf("Out of memory\n");
            break;
        case GLOB_ABORTED:
            printf("Reading error\n");
            break;
        case GLOB_NOMATCH:
            printf("No files found\n");
            break;
        default:
            break;
        }
    
        char partname[255];
        sprintf(partname, "%s", globbuf.gl_pathv[1]);
        //
        FILE *fWrite;
        fWrite = fopen(filename, "ab");
        // get fileSize
    
        unsigned int i;
        unsigned long lSize;
    
        for (i = 0; i < globbuf.gl_pathc; i++) {
            printf("\n-------%s-------\n", globbuf.gl_pathv[i]);
            FILE *fRead;
            char *buffer;
            lSize = get_file_size(globbuf.gl_pathv[i]);
            buffer = malloc(lSize);
            fRead = fopen(globbuf.gl_pathv[i], "rb");
            fread(buffer, 1, lSize, fRead);
            fwrite(buffer, 1, lSize, fWrite);
            fclose(fRead);
            free(buffer);
        }
        fclose(fWrite);
        return 0;
    
    }

    - Về phần nhập xuất màn hình theo argument ở đây chúng ta dùng hàm print_usage để in ra các dòng hướng dẫn của chương trình.
    Code:
    /* * procedure.c
     *
     *  Created on: Jul 31, 2012
     *      Author: sepdau
     */
    #include "stdio.h"
    #include "stdlib.h"
    #include "procedure.h"
    
    
    void print_usage(FILE *stream, int exit_code) {
        fprintf(stream,
                "Split [s] or Join [j] File [inputfile ...] [size | part]\n");
        fprintf(stream, "\t-h \t--help \tDisplay this help and exit\n"
                "\t-s \t--split=SIZE  \tSplit File by Size\n"
                "\t-S \t--split-part=PART \tSplit File Into PART\n"
                "\t-J \t--join \tJoin File\n"
                "\t-v \t--version\n");
        exit(exit_code);
    }
    - Tiếp theo là các file header nơi khai báo những function đã viết.

    Code:
    /*
     * merge.h
     *
     *  Created on: Jul 31, 2012
     *      Author: sepdau
     */
    
    #ifndef MERGE_H_INCLUDE
    #define MERGE_H_INCLUDE
    
    // function here
    unsigned long get_file_size(char *filename);
    int merge_file_by_parttern(char *filename);
    
    #endif

    Code:
    #ifndef SPLIT_H_INCLUDE
    #define SPLIT_H_INCLUDE
    
    // function
    
    unsigned long get_file_size(char *filename);
    int split_by_part(char *filepartName, unsigned int part);
    
    
    
    #endif /* SPLIT_H_ */


    Code:
    /*
     * procedure.h
     *
     *  Created on: Jul 31, 2012
     *      Author: sepdau
     */
    
    #ifndef PROCEDURE_H_INCLUDE
    #define PROCEDURE_H_INCLUDE
    
    void print_usage(FILE *stream, int exit_code);
    
    #endif /* PROCEDURE_H_ */

    - Để sử dụng chương trình bằng command argument như Linux thì bạn phải cần đọc cuốn Advanced Linux Prgramming, Page 20,Chapter 2, 2.1.3 Using getopt_long.

    Tiện thể giới thiệu sơ qua cho các bạn về thư viện "getopt.h" http://www.gnu.org/software/gnulib/manual/html_node/getopt_002eh.html
    Thư viện này cung cấp cho ta các struct để thực hiện bắt các argument của command nhập vào
    chủ yếu dựa trên 2 struct sau

    Code:
    const char* const short_options;
        const struct option long_options;
    - short_options dùng để bắt các option no_argument
    Một số chú ý trong code với option có required_argument thì phải có dấu ":" ở sau

    Code:
    const char* const short_options = "hs:S:j:v";
    - long_options dùng để bắt các option required_arguent

    Code:
    const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, {
                "split", required_argument, NULL, 's' }, { "split_by_size",
                required_argument, NULL, 'S' }, { "join", required_argument, NULL,
                "j" }
    Để bắt được các options khi ta thực hiện
    Code:
    next_option = getopt_long(argc, argv, short_options, long_options,
                    NULL);

    - Phần tiếp theo là Build và Install vào máy mình và người khác.
    Requirement: GCC, GNU-Autotools.
    Ví dụ trên Ubuntu có thể dùng lệnh sau để cài đặt:
    Code:
    sudo apt-get install gcc autocof automake libtool
    sau khi cài đặt xong rồi chúng ta mới đi tiếp.

    trước tiên gom những file source code (*.h, *c) của mình đã làm vào thư mục src.
    tiếp theo chúng ta thử biên dịch bằng gcc và chạy thử.
    Quá trình biên dịch bằng gcc gồm 2 bước sau

    Code:
    gcc -c *.c
    gcc -o main *.o
    sau khi chạy 2 dòng sau chúng ta đã có chương trình main và có thể chạy thử bằng cách sử dụng
    Code:
    ./main -h 
    ./main -v 
    .....
    Để test.

    Như vậy là chúng ta đã hoàn thành bước đầu tiên là đã buid và chạy được vào máy của mình. Nhưng chưa cài được.

    Để cài đặt trên máy mình or máy người khác thì chúng ta phải đóng gói phần mềm chúng ta viết lại + phần mềm trong linux có một số điểm đặc biệt về việc dùng chung thư viện cũng như một số thứ khác nên việc đóng gói bằng tay sẽ gặp khó khăn và không chuyên nghiêp (thực ra với source code đơn giản thì có thể dùng được bằng cách đơn giản tạo Makefile) nên GNU đã tạo ra bộ công cụ chuẩn là GNU AutoTools để hỗ trợ chúng ta làm điều này.
    Giới thiệu qua mời các bạn đọc sơ tài liệu ở đây http://autotoolset.sourceforge.net/tutorial.html#SEC40
    Quote lại cho nhanh đọc như này dễ hiểu hơn là dịch.
    1. Autoconf produces a configuration shell script, named `configure', which probes the installer platform for portability related information which is required to customize makefiles, configuration header files, and other application specific files. Then it proceeds to generate customized versions of these files from generic templates. This way, the user will not need to customize these files manually.
    2. Automake produces makefile templates, `Makefile.in' to be used by Autoconf, from a very high level specification stored in a file called `Makefile.am'. Automake produces makefiles that conform to the GNU makefile standards, taking away the extraordinary effort required to produce them by hand. Automake requires Autoconf in order to be used properly.
    3. Libtool makes it possible to compile position independent code and build shared libraries in a portable manner. It does not require either Autoconf, or Automake and can be used independently. Automake however supports libtool and interoperates with it in a seamless manner.
    4. Autotoolset helps you develop portable source code that conforms to the GNU coding standards by generating various boilerplate files from which you can plunge into developing your software.

    Một tool thú vị trong bộ GNU Autools đó là autoscan. Tool này rất thông minh sẽ tự động scan source code và đưa ra cho chúng ta những gợi ý thiết thực cho việc configuration hỗ trợ việc đóng gói.
    autoscan is used to detect potential portability problems with alredy written programs. That is, it scans the programms and finds, for example, functions that are not installed on all platforms.Running autoscan checks configure.ac (or alternatively configure.in) for completeness and produces aautoscan is used to detect potential portability problems with alredy written programs. That is, it scans the programms and finds, for example, functions that are not installed on all platforms.Running autoscan checks configure.ac (or alternatively configure.in) for completeness and produces a configure.scan which gives hints as to what needs to go into the configure.ac.

    The produced configure.scan can then be modified into a suitable configure.ac. which gives hints as to what needs to go into the configure.ac.


    The produced configure.scan can then be modified into a suitable configure.ac.

    Đọc ở trên ta thấy rằng sản phẩm khi chạy autoscan sẽ là file configure.scan và hình thù file đó nó như thế này
    Code:
    AC_PREREQ([2.68])AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
    AC_CONFIG_SRCDIR([src/merge.c])
    AC_CONFIG_HEADERS([config.h])
    
    
    # Checks for programs.
    AC_PROG_CC
    
    
    # Checks for libraries.
    
    
    # Checks for header files.
    
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    
    # Checks for library functions.
    AC_FUNC_MALLOC
    
    
    
    AC_OUTPUT


    Một số giải thích nhanh như sau:

    AC_PREREQ([2.68]):
    AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS]): Chứa thông tin của package gồm: FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS

    AC_CONFIG_SRCDIR([src/merge.c]): Source folder ở đâu, ở đây chúng ta cho source nằm trong folder src/ và file chính của chương trình là main.c nên chúng ta phải chú ý ở đây vì đây là file nó tự động detect nên có thể không chính xác và đây là trường hợp đó.

    AC_CONFIG_HEADERS([config.h]): cái này để nguyên vì đây là config.h tự động sinh ra chứ không phải là bỏ các file header của ta viết vào đây.


    # Checks for programs.
    AC_PROG_CC


    # Checks for libraries.


    # Checks for header files.


    # Checks for typedefs, structures, and compiler characteristics.


    # Checks for library functions.
    AC_FUNC_MALLOC: cái này oánh dấu là chúng ta đã xài hàm malloc trong chương trình nên phải khai báo cho nó biết.



    AC_OUTPUT
    vậy chúng ta sẽ sao chép file này qua file configure.ac thôi. Có một chút chỉnh sửa và thêm như sau

    Code:
    AC_PREREQ([2.68])[B]AC_INIT(sjf, 0.0.1, giaosudau@gmail.com)[/B]
    [B]AM_INIT_AUTOMAKE[/B]
    [B]AC_CONFIG_SRCDIR([src/main.c])[/B]
    AC_CONFIG_HEADERS([config.h])
    
    
    # Checks for programs.
    AC_PROG_CC
    
    
    # Checks for libraries.
    
    
    # Checks for header files.
    
    
    # Checks for typedefs, structures, and compiler characteristics.
    
    
    # Checks for library functions.
    AC_FUNC_MALLOC
    [B]AC_CONFIG_FILES([Makefile])[/B]
    AC_OUTPUT

    Những dòng bôi đậm ở trên là những dòng đã được thay đổi or thêm. Điều này các bạn sẽ detect khi chạy lệnh
    Code:
    aclocal
    aclocal must be run in order to create aclocal.m4. As far as I can see, the idea is to put all m4 macros used for deployment into aclocal.m4 so that the machines, on which a program is going to be installed, are not dependent on automake and the like.


    Tiếp theo chúng ta sẽ tạo file Makefile.am
    Makefile.am is processed by automake.
    The commands in a Makefile.am file mostly look like variable assignment.

    Hình thù của file này khá đơn giản
    Code:
    bin_PROGRAMS = sjf 
    sjf_SOURCES = main.c split.c split.h merge.c merge.h procedure.c procedure.h
    dòng 1 bin_PROGRAM là chương trình biên dịch của mình có tên là gì?
    dòng 2 (tên_chương_trình)_SOURCES = liệt kê tất cả các file đã viết ra bao gồm cả file header.

    Tiếp theo chúng ta chạy lệnh
    Code:
    [COLOR=#10205F]autoheader[/COLOR]
    autoheader can be used to create an initialtemplate header file: config.h.in (or config.in, see AM_CONFIG_HEADER)
    Để có 1 gói chuẩn thì chúng ta cần có những file này NEWS README AUTHORS ChangeLog.
    Vì vậy chúng ta sẽ tạo nó
    Code:
    [COLOR=#10205F]touch NEWS README AUTHORS ChangeLog[/COLOR]
    bước tiếp theo ta chạy lên
    Code:
    automake -a
    Bước tiếp là
    autoconf is part of the autoconf package.autoconf determines what the system is capable of. That is, which function exist and the like. Additionally, it finds the location of programms needed to build a program. It is part of autotools.
    autoconf produces the configure script.
    Code:
    autoconf
    để tạo ra package của phần mềm ta viết. Tới đây coi như hoàn thành ->> nhưng chưa biết có chạy được không )

    Bước tiếp là testing
    chúng ta sẽ chạy thử lệnh sau
    Code:
    ./confugure
    Nếu kết quả ra như thế này thì có thể đúng
    Code:
    [LEFT][COLOR=#333333][FONT=lucida grande]sepdau@vnexplorer:~/workspace/[/FONT][/COLOR][COLOR=#333333][FONT=lucida grande]split$ ./configure [/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking for a BSD-compatible install... /usr/bin/install -c[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking whether build environment is sane... yes[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking for a thread-safe mkdir -p... /bin/mkdir -p[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking for gawk... gawk[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking whether make sets $(MAKE)... yes[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking for gcc... gcc[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking whether the C compiler works... yes[/FONT][/COLOR]
    [COLOR=#333333][FONT=lucida grande]checking for C compiler default output file name.[/FONT][/COLOR][COLOR=#333333][FONT=lucida grande].. a.out
    checking for suffix of executables... 
    checking whether we are cross compiling... no
    checking for suffix of object files... o
    checking whether we are using the GNU C compiler... yes
    checking whether gcc accepts -g... yes
    checking for gcc option to accept ISO C89... none needed
    checking for style of include used by make... GNU
    checking dependency style of gcc... gcc3
    checking how to run the C preprocessor... gcc -E
    checking for grep that handles long lines and -e... /bin/grep
    checking for egrep... /bin/grep -E
    checking for ANSI C header files... yes
    checking for sys/types.h... yes
    checking for sys/stat.h... yes
    checking for stdlib.h... yes
    checking for string.h... yes
    checking for memory.h... yes
    checking for strings.h... yes
    checking for inttypes.h... yes
    checking for stdint.h... yes
    checking for unistd.h... yes
    checking for stdlib.h... (cached) yes
    checking for GNU libc compatible malloc... yes
    configure: creating ./config.status
    config.status: creating Makefile
    config.status: creating config.h
    config.status: executing depfiles commands[/FONT][/COLOR][/LEFT]
    Để đem cài thì các bác đóng thành tar rồi share cho người khác
    lệnh cài đơn giản gồm 3 bước
    Code:
    ./configure
    make 
    sudo make install

    Tiếp tục chỉnh lý!
    Attached Files
    Last edited by 09520017; 01-08-2012, 20:58.
    sepdau@sepdau-K42JE:~$ python
    Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
    [GCC 4.5.2] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print 'Hãy góp ý theo văn hóa nguồn mở!'

    Code:
        _                   .-=-.          .-==-.
       { }      __        .' O o '.       /  -<' )
       { }    .' O'.     / o .-. O \     /  .--v`
       { }   / .-. o\   /O  /   \  o\   /O /
        \ `-` /   \ O`-'o  /     \  O`-`o /
    jgs  `-.-`     '.____.'       `.____.'

  • #2
    Oh hâm mộ group OSS quá!
    Blog Hứa Anh Tôi trên mạng xã hội: Hứa Anh + | FaceBook | Thủ thuật Máy tính

    Comment


    • #3
      - Phần cài đặt gồm có: aclocal.m4 config.h.in configure.ac~ Makefile README
      AUTHORS config.h.in~ configure.scan Makefile.am src
      autom4te.cache config.log COPYING Makefile.am~ stamp-h1
      autoscan.log config.status depcomp Makefile.in
      ChangeLog configure INSTALL missing
      config.h configure.ac install-sh NEWS
      anh dùng autoconf ah +_+.
      Be different and always different
      http://archlinuxvn.org/
      http://theslinux.org
      http://lab.infosec.xyz

      Comment


      • #4
        Mới bổ sung thêm phần build + source code.

        Originally posted by 10520058 View Post
        anh dùng autoconf ah +_+.
        Chú mày nên đọc kỹ những gì anh viết ít nhất cũng google những gì đọc được nếu chưa hiểu chứ không comment dạng thiếu google như thế này.
        sepdau@sepdau-K42JE:~$ python
        Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53)
        [GCC 4.5.2] on linux2
        Type "help", "copyright", "credits" or "license" for more information.
        >>> print 'Hãy góp ý theo văn hóa nguồn mở!'

        Code:
            _                   .-=-.          .-==-.
           { }      __        .' O o '.       /  -<' )
           { }    .' O'.     / o .-. O \     /  .--v`
           { }   / .-. o\   /O  /   \  o\   /O /
            \ `-` /   \ O`-'o  /     \  O`-`o /
        jgs  `-.-`     '.____.'       `.____.'

        Comment


        • #5
          Originally posted by 09520017 View Post
          Mới bổ sung thêm phần build + source code.


          Chú mày nên đọc kỹ những gì anh viết ít nhất cũng google những gì đọc được nếu chưa hiểu chứ không comment dạng thiếu google như thế này.
          Bài viết hay
          Last edited by 10520058; 01-08-2012, 22:12.
          Be different and always different
          http://archlinuxvn.org/
          http://theslinux.org
          http://lab.infosec.xyz

          Comment


          • #6
            Nhận được quyết định của anh An:
            Originally posted by truonganpn
            Các bạn tạo topic post kết quả mình đã làm được lên forum, làm được gì post đó, lỗi gặp phải hay vướng mắc gì cũng post lên tuần sau mình offline giải quyết.

            Do vậy mình quyết định làm cái việc mà lẽ ra đã làm hôm thứ 3 tuần trước rồi đó là đưa code và những gì đã cũng như chưa làm được lên đây muốn chia sẻ cùng các bạn trong thread này cho tiện để cùng hoàn thiện và tìm ra giải pháp tốt nhất cho bài toán Nối và Ghép file (Merge & Split file) hay (Join & Split file như anh Chánh cũng vậy) và để CLB hoạt đông Online sôi nổi hơn. Lý do ở đây là mình vẫn chưa tạo được bản cài đặt bằng tập hợp lệnh:

            PHP Code:
            $ ./confnigure
            make
            make install 
            Không sao, phần này chắc phải nhờ anh Chánh hướng dẫn thêm, còn bây giờ mình sẽ nói rõ hơn về yêu cầu cũng như hướng giải quyết của bài toán Merge & Split file bằng C/C++ trên Linux một cách đơn giản hơn - ý tưởng của anh Chánh đưa ra chưa tối ưu lắm - nếu như mình chia một file 3GB làm 2 phần bằng nhau thì buffer sẽ được cấp phát là 1.5GB, tốn tài nguyên -> không cấp phát được hoặc nếu có(RAM 4GB) thì máy cũng sẽ shout down nhanh chóng .

            A - NHỮNG ĐIỀU ĐÃ LÀM ĐƯỢC:

            I - YÊU CẦU:
            Nghe cái tên thôi chắc các bạn cũng đoán được chương trình này phải có đủ 2 chức năng cơ bản nốicắt được file viết bằng C/C++ trên Linux nghe chừng có vẻ khó nhỉ nhưng sau đây mình sẽ giúp các bạn thấy không phải vậy.

            1. Đầu tiên mình xin nói về ngôn ngữ cũng như công cụ cần thiết:

            Về ngôn ngữ, C là ngôn ngữ lý tưởng nhất trong trường hợp này bởi ai trong chúng ta cũng đã học qua Tin học đại cương, chương trình này chúng ta chỉ cần sử dụng các thư viện chuẩn sau:

            Code:
            # include <stdlib.h>    //free(), malloc() để quản lý bộ nhớ và cấp phát động.
            # include <stdio.h>     //Lấy các hàm thao tác với file: fopen(), fread(), fwrite() hay fprintf() và sprintf() giúp thao tác với chuỗi rất tiện lợi.
            # include <string.h>    //Lấy các hàm thao tác với chuỗi như strcpy(), strcmp(), strcat(), atoi(), strlen().
            # include <time.h>      //Dùng hàm clock() để rính thời gian chạy của chương trình.
            # include <sys/stat.h> //Lấy hàm stat() giúp mình viết hàm fsize(FILE*) lấy kích thước file, kiểm tra sự tồn tại của file, thư mục.
            # include <getopt.h>    //Cũng hơi lạ, nó có hàm getopt_long() giúp phân tích đối số nhập vào từ dòng lệnh theo đúng [B]Chuẩn[/B].
            2. Về công cụ thì khuyên dùng "eclipse CDT" cái này có hỗ trợ debug rất là tuyệt - đúng như anh Chánh đã "(recommend)"

            II - PHÂN TÍCH YÊU CẦU VÀ HƯỚNG GIẢI QUYẾT:

            Thế là xong vấn đề ngôn ngữ và công cụ chúng ta bắt tay ngay vào việc phân tích yêu cầu và hướng giải quyết ngay:

            1. Đầu tiên chúng ta phải nói đến việc nối và cắt file cần những đối số nào đã:
            a) Để cắt file chúng ta cần:
            - Tên và đường dẫn đầy đủ file cần Split.
            - Số part cần chia.
            - Kích thước mỗi part là bao nhiêu, có thể là bằng nhau hoặc có thể do người dùng nhập vào
            - Thư mục lưu các part sau khi Split (thực ra là tạo thêm các file nhỏ khác file nguồn vẫn còn nguyên không hề hấn gì).
            - Ngoài ra còn có thể thêm một số tùy chọn nữa như là:

            + Đặt password để mã hóa file nữa.
            + Có thể thêm tham số là có xóa file nguồn sau khi chia hay không.


            b) Để ghép file chúng ta cần:
            - Tên và đường dẫn đầy đủ của một trong số các part *.1, *.2 hay *.n đều được.
            - Cũng cần thư mục nơi tạo file sau khi ghép
            - Ngoài ra còn có thể thêm một số tùy chọn nữa như là:

            + Lấy password để giải mã file nữa.
            + Có thể thêm tham số là có xóa các file nguồn *.1, *.2,... sau khi nối hay không.


            2. Làm sao để lưu trữ các tham số:

            Có nhiều đối số thế thì chúng ta phải viết hàm Merge(), Split() có bao nhiêu tham số đây trong khi có những tham số không phải lúc nào cũng dùng. Để cho gọn đơn giản dễ chỉnh sửa sau này chúng ta sẽ tạo một struct chung dùng làm đối số cho cả hai hàm Split() và Merge() và cũng là nơi ghi nhận các đối số nhập vào từ dòng lệnh của người dùng mà ta phân tích được đó là struct stArgument(mình tự tạo thêm trong mã nguỗn đính kèm mình chú thích bằng tiếng Anh).


            PHP Code:
            // Define structure contain
            typedef struct {

                
            unsigned long long aCustomSizes[MAX_PART_SPLIT];     // Mảng lưu trữ kích thước mỗi part.
                
            int deleteSourceFile;        // Có xóa file nguồn sau khi cắt hay ghép xong hay không.
                
            int equalSize;            // Các part có kích thước bằng nhau hay không.
                
            int iCustomSize;            //  Số lượng các phần từ trong mảng aCustomSizes bên trên.
                
            charinput_file;            // Con trỏ trỏ đến chuỗi lưu đường dẫn tới file nguồn.
                
            charoutput_directory;    // Chuỗi lưu thư mục xuất kết quả.
                
            charpasswd;            // Chuỗi lưu mật khẩu.
                
            int quantityPart;            // Số mảnh cẩn chia.
                
            int merge;                // Người dùng muốn nối file -dùng để kiểm tra sự chính xác các đối số nhập từ dòng lệnh sau này
                
            int split;                // Người dùng muốn ghép file-dùng để kiểm tra sự chính xác các đối số nhập từ dòng lệnh sau này
                    
            eUnit unit;                // Đơn vị của kích thước part có thể là B, KB, MB, GB
            stArgument
            enum eUnit để định nghĩa giá trị các hằng số của đơn vị B, KB, MB, GB:
            PHP Code:
            typedef enum  UNIT
            {
                
            1,
                
            KB 1024,
                
            MB 1024 1024,
                
            GB 1024 1024 1024,
                
            NU 0
            eUnit
            3. Lấy các tùy chọn và tham số từ dòng lệnh:

            Như vậy là chúng ta đã biết cần những đối số nào cho việc cắt và ghép file rồi, bây giờ chúng ta sẽ lấy nó từ dòng lệnh mà người dùng nhập vào. Trước tiên bạn phải biết dạng chuẩn của nó thế nào đã:

            a) Cấu tạo tham số trên dòng lệnh:

            [Tên chương trình] [Tùy chọn 1] [tham số nếu cần cho Tùy chọn 1] ... [Tùy chọn n] [tham số nếu cần cho Tùy chọn n]

            Trong đó:
            + Dấu ... nghĩa là còn có nhiều [Tùy chọn i] [tham số nếu cần cho Tùy chọn i] ở giữa. Chú ý là các cặp [Tùy chọn] tham số này] không quan tâm đến thứ tự cái nào trước cũng được --> rất uyển chuyển.
            + [Tùy chọn] chia làm 2 loại đó là: Long option ví dụ --helpShort option ví dụ -h hai tùy chon --help và -h là tương
            đương nhau.
            + Đi sau một tùy chọn có thể không có tham số nếu tùy chọn đó thuộc dạng luận lý tức là có hoặc không.
            Ví dụ: --delete hay -d là tùy chọn xóa file nguồn sau khi cắt/ghép xong chẳng hạn nếu không chọn nó thì sẽ không xóa file nguồn.
            + Đi sau một tùy chọn có thể cần tham số đi kèm.
            Ví dụ: --split <file name> hay -s <file name> tức là mình muốn Split (chia) file với đường dẫn tới file là <file name>
            chúng ta cần lưu nó lại để truyền cho hàm Merge() và Split() nữa.

            b) Cách thức làm việc:

            - OK vậy là các bạn biết cách chúng ta yêu cầu chương trình làm việc thông qua dòng lệnh rồi nhé, bây giờ chúng ta sẽ tìm hiểu làm sao để chương trình có thể nhận biết tất cả các [Tùy chọn] và lấy đối số khi cần thiết một cách uyển chuyển như trên.

            + Hàm main của chúng ta có dạng như sau:

            PHP Code:
             int main (int argccharargv[]) 
            - Khi nhập tham số như trên thì: argc ( argument count) - là số tham số nhập vào kể cả tên chương trình.
            - Còn argv (argument value) - là một mảng các chuỗi mà mỗi phần tử trong mảng là một Tùy chọn/tham số ở trên

            + OK mình nghĩ các bạn cũng có thể tự viết một hàm để phân tích và lấy ra các đối số như trên nhưng:
            - Hàm đó đã nằm trong thư viện chuẩn <getopt.h> rồi .
            - Lập trình là phải có sự kế thừa và phát huy chúng ta không cần phát minh lại bánh xe (reinvent wheel)
            --> Tốn thời gian, "trong lúc đó người ta đã viết xong hàm chia file rồi" - câu này trích lời anh An.

            + Hàm mà mình đang nhắc đến ở đây là: int getopt_long (argc, argv, short_options, long_options, NULL); mình học được trong cuốn advanced-linux-programming.pdf các bạn copy tên sách bỏ vào google một cái là ra direct link ngay sau đây mình sẽ giới thiệu cụ thể thông qua hàm main của mình.
            Đầu tiên các bạn cần biết cụ thể các đối số và những gì cần nhập trên dòng lệnh trước cái đã, mình lấy kết quả khi gọi hàm Help() của mình - một chương trình tốt luôn đi kèm phần trợ giúp - Trong code mình viết bằng tiếng Anh

            [LeAnh@LeAnh Debug]$ ./MSF -h
            Hướng dẫn đầy đủ:

            -h --help Hiển thị trợ giúp.
            -v --version Hiển thị phiên bản của chương trình.
            -l --license Hiển thị giấy phép và các điều khoản sử dụng chương trình.
            -a --about Hiển thị thông tin về tác giả.

            Sử dụng: MSF [Tùy chọn] <Tham số nếu cần>

            Để nối file bạn có thể sử dụng tùy chọn dưới đây:
            MSF -m <Tập tin.1> -o <Thư mục xuất kết quả>

            -m <file> --merge <file> Hợp nhất tất cả các <file> có tên theo định dạng [Tên file].[i] trong thư mục chứa file đó.

            Để chia file bạn cần sử dụng các tùy chọn sau:
            MSF -s <Tên tập tin.1> -q <Tham số là một số nguyên dương> -o <Thư mục xuất kết quả>

            -s <file> --split <file> Đường đẫn tới file cần chia

            -q <integer> --quantity <số nguyên dương> Số part cần chia là một số nguyên dương nhỏ hơn 100.
            -u <string> --unit <chuỗi> Đơn vị của kích thước: B, KB, MB hoặc GB với B là đơn vị mặc định
            -c <integer> --custom <số nguyên dương> Chia file của bạn có kích thước tùy chọn với đơn vị ở trên- có thể gọi nhiều lần.
            -e --equal Chia file nguồn thành các part có kích thước bàng nhau, nó là mặc định không gọi cũng được

            Các tùy chọn hữu ích khác dùng cho cả Chia và ghép file:

            -o <directory> --output <Thư mục> Thư mục xuất kết quả
            -p <string> --passwd <chuỗi> Đặt/Nhập password để Mã hóa/Giải mã file của bạn, đọ dài không quá 30.
            -d --delete Xóa file nguồn đi sau khi Chia/Ghép xong.

            Báo lỗi qua: greendream.ait@gmail.com
            Các bạn thấy nó có nhiều tùy chọn cần xử lý, để đơn giản và dễ dàng nâng cấp khi muốn thêm chức năng mà không cần thay đổi khai báo của các hàm như Split() hay Merge() - vì vậy đây chính là một lý do nữa cho việc dùng stArgument để nhập và truyền đối số trong chương trình.


            Trong phần này ví dụ: -oShort option, -outputLong option bạn sử dụng một trong hai cái, cái nào cũng được.
            Dưới đây là chi tiết làm thế nào để chương trình lấy được tất cả các tùy chọn trên với hàm int getopt_long (argc, argv, short_options, long_options, NULL);

            Đầu tiên là argc, argv các bạn khai báo chương trình chính như sau:

            PHP Code:
            // The main program.
            int main (int argcchar **argv)
            {
                  
            // Code here

            Hàm được gọi nhiều lần, mỗi lần nó sẽ xử lý một tùy chọn trong argv, xác định nó là tùy chọn nào , xem có cần tham số hay không, nếu có thì lấy bằng cách cho con trỏ optarg trỏ đến tham số đó để ta có thể lấy ra, hàm getopt_long() đơn giản là trả ra cho chúng ta cái Short option để chúng ta biết nó là tùy chọn nào ví dụ nó xử lý đến --help hay -h nó sẽ trả ra ký tự 'h' chuyển sang kiểu int nó là 104 vì vậy chúng ta cần biến:
            PHP Code:
            int next_option;

            ..........

            // Lát nữa lấy tùy chọn kế tiếp
            next_option getopt_long (argcargvshort_optionslong_optionsNULL); 
            Còn đối số short_options nó được khai báo như sau:
            PHP Code:
            // Là một danh sách các ký tự hợp lệ cho tùy chọn ngắn - short_option.
            const char* const short_options "hvladeq:c:p:o:m:s:u:"
            Nó là một chuỗi các ký tự trong bảng chữ cái (phân biệt in hoa và in thường) đại diện cho Short option
            Các bạn đã biết tùy chọn thì có thể cần hoặc không cần tham số:

            Ví dụ:
            - Tùy chọn -h để xuất trợ giúp thì không cần tham số đi kèm nên trong chuỗi short_options chỉ cần khai báo là h.
            - Nhưng tùy chọn -o cần tham số là thư mục xuất kết quả nên bạn khai báo là o:

            Còn đối số long_options nó lại là cả một struct được khai báo như sau:
            PHP Code:
            // Là một mảng các struct mô tả Long option
            const struct option long_options[] = {
              { 
            "help",        0NULL'h' }, // Khai báo tùy chọn help có Long option là --help Short option là -h và số 0 tức là không cần tham số
              
            "version",    0NULL'v' },
              { 
            "license",    0NULL'l' },
              { 
            "about" ,    0NULL'a' },
              { 
            "delete",    0NULL'd' },
              { 
            "equal",     0NULL'e' },
              { 
            "quantity"1NULL'q' }, // Khai báo tùy chọn quantity có Long option là --quantity Short option là -q và số 1 tức là cần tham số đi kèm
              
            "custom",   1NULL'c' },
              { 
            "passwd",   1NULL'p' },
              { 
            "output",    1NULL'o' },
              { 
            "merge",     1NULL'm' },
              { 
            "split",        1NULL's' },
              { 
            "unit",        1NULL'u' },
              { 
            NULL,         0NULL,  0  // Kết thúc luôn cần một khai báo kiểu này
            }; 

            Vậy chúng ta cần gọi hàm getopt_long bao nhiêu lần để lấy hết các Tùy chọn cũng như tham số đi kèm của người dùng ? Có phải là argc ?
            Cũng không phải, câu trả lời là không biết trước được, mà đã không biết trước được thì ta sẽ dùng vòng lặp do-while hay while, ở đây mình dùng do-whie cho tiện, sau khi khai báo tất cả các tham số như trên bạn hãy dùng vòng lặp sau để quét hết các tham số:


            Nhưng trước tiên chúng ta phải khai báo và khởi tạo các thông số mặc định cho đối số nhập vào đã chứ nhỉ:
            PHP Code:
                stArgument _UserArg;

                
            _UserArg.deleteSourceFile  0;
                
            _UserArg.equalSize           0;
                
            _UserArg.iCustomSize        0;
                
            _UserArg.input_file           NULL;
                
            _UserArg.output_directory NULL;
                
            _UserArg.passwd             NULL;
                
            _UserArg.quantityPart       0;
                
            _UserArg.unit                  B;
                
            _UserArg.merge               0;
                
            _UserArg.split                  0
            Còn đây là vòng lặp để nhập các tham số dòng lệnh của người dùng
            PHP Code:
            do
                {

                  
            // Lấy tùy chọn kế tiếp
                  
            next_option getopt_long (argcargvshort_optionslong_optionsNULL);

                      
            // Chia trường hợp các tùy chọn để nhập tham số.
                  
            switch( next_option )
                  {
                      case 
            'h':
                          
            Help (stdout);
                          break;

                      case 
            'v':
                          
            Version (stdout);
                          break;

                      case 
            'l':
                          
            Licence (stdout);
                          break;

                      case 
            'a':
                          
            About (stdout);
                          break;

                      case 
            'd':
                          
            _UserArg.deleteSourceFile 1;
                          break;

                      case 
            'e':
                          
            _UserArg.equalSize 1;
                          break;

                      case 
            'q':
                          
            _UserArg.quantityPart atoi (optarg);
                          break;

                      case 
            'c':
                          
            _UserArg.aCustomSizes[_UserArg.iCustomSize] = atoi (optarg);
                          
            _UserArg.iCustomSize++;
                          break;

                      case 
            'p':
                          
            _UserArg.passwd optarg;
                          break;

                      case 
            'o':
                          
            _UserArg.output_directory optarg;
                          break;

                      case 
            'm':
                          
            _UserArg.merge 1;
                          
            _UserArg.input_file optarg;
                          
            // Set task equal merge.
                          
            task MERGE;
                          break;

                      case 
            's':
                          
            _UserArg.split 1;
                          
            _UserArg.input_file optarg;
                          
            // Set task equal split
                          
            task SPLIT;
                          break;

                      case 
            'u':
                          if (
            strcmp (optarg"B") == 0)
                            
            _UserArg.unit B;
                          else
                            if (
            strcmp (optarg"KB") == 0)
                              
            _UserArg.unit KB;
                            else
                              if (
            strcmp (optarg"MB") == 0)
                                
            _UserArg.unit MB;
                              else
                                  if (
            strcmp (optarg"GB") == 0)
                                      
            _UserArg.unit GB;
                                  else
                                      
            _UserArg.unit NU// That mean No Unit.
                          
            break;

                      case 
            '?':
                          
            fprintf (stderr"Bạn đã định nghĩa một tùy chọn không hợp lệ, sử dụng [--help] or [-h] để tìm hiểu nhiều hơn !\n");
                          break;

                      case -
            1:
                        
            // Hoàn thành việc quét tùy chọn
                        
            break;

                      default:
                        
            abort ();
                        break;
                    }

                  } while ( 
            next_option != -1); 
            Chý ý là getopt_long trả ra '?' nếu tham số người dùng nhập vào sai chuẩn còn -1 là hết tham số rồi và vòng lặp cũng kết thúc ở đây.
            Trong này mình có dùng thêm một enum khái báo các hằng số giúp bạn có thể nhận biết một cách đơn giản nhiệm vụ cần làm đó là eTask:
            PHP Code:
            typedef enum  TASK {MERGESPLITNONEeTask;
            eTask task       NONE
            Mặc định lúc khởi tạo nó là NONE. Trong vòng lặp trên nếu người dùng muốn chia file thì mình cho task = SPLIT;
            PHP Code:
             case 's':
                        
            _UserArg.split 1;
                    
            _UserArg.input_file optarg;
                    
            // Set task equal split
                    
            task SPLIT;
                 break; 
            Ngay sau khi có được các tham số cũng như tùy chọn rồi ta phải kiểm tra sụ đúng đắn của nó và báo lỗi cho người dùng biết còn sửa.
            Hàm getopt_long chỉ kiểm tra các tham số nhập vào về mặt cú pháp thôi, còn nội dung các tham số có đúng theo yêu cầu của chương trình hay không thì chúng ta phải tự kiểm tra lấy các bạn có thể xem chi tiết hàm này trong file đính kèm.

            PHP Code:
                  if (TestUserArgError (task_UserArg) != 1)
                  {
                      return 
            0;
                  } 
            Nếu không có lỗi thì tùy vào task tức nhiệm vụ mà người dùng muốn làm là gì ta sẽ thực hiện chúng:


            Đến đây chúng ta có thể bấm giờ cho hệ thống bằng hàm clock() trong thư viện <time.h> rồi.
            PHP Code:
                  clock_t startfinish;
                  
            start clock(); 
            Thực hiện nhiệm vụ:
            PHP Code:
                  switch (task)
                  {

                    case 
            MERGE:
                        
            Merge (_UserArg);
                      break;

                    case 
            SPLIT:
                        
            Split (_UserArg);
                      break;

                    case 
            NONE:
                        
            // Do nothing
                        
            break;
                  } 
            [B]Xong rồi xuất thời gian thực hiện cho người dùng biết:
            PHP Code:
                  finish clock ();

                
            // Return time elapsed unit mili second
                
            unsigned long long elapsed= (unsigned long long) ((finish -start)/CLOCKS_PER_SEC);
                
            int hour elapsed / (3600);    elapsed %= (3600);
                
            int min elapsed / (60);    elapsed %= (60);

                
            fprintf(stdout"\nTime elapsed is: %2d:%2d:%2d\n"hourminelapsed); 
            Trên đây là cách thức đề các bạn có được một chương trình nhập tham số từ dòng lệnh theo đúng chuẩn rồi đó !

            III - CHI TIẾT Ý TƯỞNG VÀ THỰC HIỆN VIỆC MERGE & SPLIT FILE

            Ý tưởng tổng quát xuất phát từ đơn vị nhỏ nhất của file đó là Byte nó là thành phần nhỏ nhất cấu thành file vì vậy chúng ta không thể chia được file rỗng cũng như file có kích thước 1B, nếu file có từ 2 B trở lên ta mới có thể chia và lúc này thật đơn giản chúng ta sẽ tính toán kích thước file đích cần tạo rồi tạo ra file mới bằng việc đọc từng byte của file nguồn ghi vào file đích với số lượng B vừa tính toán được cụ thể cho từng trường hợp

            1. Chia file:

            - Chúng ta cần xác định kích thước mỗi part:
            + Thông qua số part cần chia _UserArg.quantityPart và kích thước tùy chọn mỗi part trong mảng _UserArg.aCustomSizes[].
            + Nếu số part có kích thước tùy chọn _UserArg.iCustomSize nhỏ hơn số part cần chia _UserArg.quantityPart thì tổng dung lượng còn lại sẽ được chia đều cho mỗi part còn lại.
            + Dù có chọn kích thước mỗi part có bằng nhau hay không chúng ta đều tính tns rồi lưu trong mảng _UserArg.aCustomSizes[] vì kích thước file khi chia cho số part có thể dư mà chỉ cần dư một byte thôi là part cuối sẽ thiếu một byte và dẫn đến lỗi.

            - Dựa vào _UserArg.output_directory_UserArg.input_file chúng ta sẽ xác định tên của part mới cần tạo dạng file_name.1 , ..., file_anme.n
            - Mở _UserArg.input_file bằng hàm fopen(_UserArg.input_file, "rw"), tạo part mới rồi copy từng byte sang part mới
            Code:
            PHP Code:
                // Open file to split
                
            FILEFSource fopen(UserArg.input_file"rb");

                
            char newPartName[1024];
                
            // Split
                
            int i;
                for (
            0UserArg.quantityParti++)
                {
                    
            // Create new part name and file with that name
                    
            newPartName[0] = '\0';
                    if (
            UserArg.output_directory != NULL)
                    {
                        if (
            UserArg.output_directory[strlen(UserArg.output_directory) -1] == '/')
                            
            UserArg.output_directory[strlen(UserArg.output_directory) -1] = '\0';

                        
            sprintf(newPartName"%s/%s.%d"UserArg.output_directoryGetFileName(UserArg.input_file), i+1);
                    }
                    else
                    {
                        
            sprintf(newPartName"%s.%d"GetFileName(UserArg.input_file), i+1);
                    }

                    
            FILEFNewPart fopen(newPartName"wb");
                    
            // Copy to new part file
                    
            unsigned long long ul;

                    for( 
            ul ul <= UserArg.aCustomSizes[i]; ul++)
                    {
                        
            char buffer;
                        
            fread  (&buffersizeof(buffer), 1FSource);
                        
            fwrite (&buffersizeof(buffer), 1FNewPart);
                    }

                    
            fclose(FNewPart);

                }

                
            fclose(FSource); 
            Code chi tiết bên dưới trong phần VI!

            2. Hợp nhất file:

            + Chúng ta cần xác định tên file sau khi hợp nhất để mở file nay ra ghi dưới dạng bit:
            - Từ tên của part nhập vào _UserArg.input_file ta xác định tên file đích bằng cách xóa đi đuôi .1.
            - Từ tên file sau khi hợp nhất chúng ta tìm part tiếp theo bằng cách tăng chỉ số part và gắn chỉ số này vào đuôi file đích
            - Kiểm tra part với tên vừa tạo ra có tồn tại hay không nếu tồng tại thì copy tùng bit cho đến khi gặp byte EOF chú ý không copy byte này vào file đích sẽ gây lỗi.
            Code
            PHP Code:
                // Merge files

                // Tạo file đích để nối các part vào
                
            FILEResultFile fopen(OutputFileName"wb");
                    
                   
            // Tìm tên của part tiếp theo
                
            partIndex 1;
                
            sprintf(NextFileName"%s.%d"UserArg.input_filepartIndex);

                while (
            1)
                {
                            
            // Kiểm tra sự tồn tại của part mới nếu tồn tại thì nối tiếp vào file đích còn không thì dừng luôn
                    
            if (CheckFileExistence(NextFileName) == 0)
                    {
                        if(
            partIndex 1)
                        {
                            
            fprintf (stderr"File \"%s\" not found !\n"NextFileName);
                            return 
            0;
                        }
                        else
                        {
                            
            // Complete
                            
            fprintf(stdout"\nMerged %d file completed output file: %s\n"partIndex -1OutputFileName);
                            break;
                        }
                    }

                            
            // Mở part ra để đọc từng bit nối vào file đích
                    
            FILEFNext fopen(NextFileName"rb");

                    
            // Copy từng bit của part bỏ vào file đích
                    
            char buffer;
                    while ( 
            feof(FNext) == )
                    {
                                    
            // Đọc một byte (kích thước của [B]buffer[/B]) trong part
                        
            fread(&buffersizeof(buffer), 1FNext);

                                    
            // Kiểm tra đọc xong part hay chưa nếu rồi thì dừng ngay chánh đọc byte EOF
                        
            if ( feof(FNext) != )
                            break;

                                    
            // Ghi byte vừa đọc xong xuống file đích
                        
            fwrite(&buffersizeof(buffer), 1ResultFile);

                    }

                    
            // Tạo tên cho part tiếp theo
                    
            partIndex++;
                    
            sprintf(NextFileName"%s.%d"UserArg.input_filepartIndex);

                }

                
            fclose(ResultFile); 
            Code chi tiết bên dưới trong phần VI!

            3. Tùy chọn thêm:

            a) Đặt mật khẩu:
            - Bạn thêm tùy chọn -p [*************] trong đó [*************] là mật khẩu của bạn.
            - Mã hóa/Giải mã từng byte buffer một.
            - Việc mã hóa và giải mã được thực hiện giữa 2 câu lệnh đọc fread() một B vào buffer kiểu char và ghi fwrite() buffer xuống file nguồn.

            PHP Code:
                                    char buffer;
                        
            fread  (&buffersizeof(buffer), 1FSource);

                        
            // Mã hóa nếu đoạn code này nằm trong hàm chia file
                        // EncodeBuffer(&buffer, UserArg.passwd);

                        // Giải mã nếu đoạn code này nằm trong hàm nối file
                        // DecodeBuffer(&buffer, UserArg.passwd);

                        
            fwrite(&buffersizeof(buffer), 1ResultFile); 
            Trong khi chia để mã hóa với mật khẩu [*************] mình viết hàm mã hóa buffer bằng cách xor tùng byte của file với một ký tự tương ứng trong mật khẩu:

            VD: bạn có file 1024 bytes mật khẩu greendream có 10 ký tự thì bạn xor như sau:
            byte[1] xor 'g', byte[2] xor 'r', byte[3] xor 'e',...,byte[10] xor 'm', byte[11] xor 'g',...,byte[20] xor 'm', byte[21] xor 'g',... đến hết.

            PHP Code:
            Encode with buffer and password
            void EncodeBuffer
            (charbufferunsigned long long buffer_sizecharpasswdunsigned long long oldOverallProgress)
            {
                if (
            passwd == NULL)
                {
                    return;
                }

                
            int i 0;
                for (
            1<= buffer_sizei++)
                {
                  (*
            buffer) ^= passwd[ (oldOverallProgress i) % strlen(passwd)];
                }
                

            Để giải mã làm tương tự như khi mã hóa xor vơi ký tự tương ứng trong mật khẩu :
            PHP Code:
            // Decode with buffer and password
            void DecodeBuffer(charbufferunsigned long long buffer_size,  charpasswdunsigned long long oldOverallProgress)
            {
                if (
            passwd == NULL)
                {
                    return;
                }
                
                
            int i 0;
                for (
            1<= buffer_sizei++)
                {
                  (*
            buffer) ^= passwd[ (oldOverallProgress i) % strlen(passwd)];
                }
                

            b) Xóa file nguồn:
            - Để xóa file chúng ta dùng tùy chọn -d hay --delete
            - Rất đơn giản sau khi đọc hết dữ liệu của file nguồn rồi ta kiểm tra xem người dùng có chọn tùy chọn này không nếu có thì dùng hàm remove(FILE*) để xóa.
            PHP Code:
                    if (UserArg.deleteSourceFile)
                {
                    
            remove(UserArg.input_file);
                } 
            IV - HIỂN THỊ TIẾN ĐỘ TRÊN DÒNG LỆNH DẠNG: Creating part 2/10 [======> ] 15% | 60%

            - Để hiển thị tiến độ đầu tiên phải xác định được số byte cần Chia/Ghép và hiện tại đang Chia/Ghép được bao nhiêu rồi để tính % hoàn thành
            + struct lưu tất cả các thông số trên:
            PHP Code:
                    // Define structure progress
                    
            typedef struct {
                
            unsigned long long    overallTaskSize;
                
            unsigned long long    subTaskSize;
                
            double                overallProgress;
                
            double                subProgress;
                
            char*                TaskName;
                
            int                    TaskNumber;
                
            int                    TaskIndex;

            stProgress
            + Chúng ta phải cung cấp các thông số như tổng kích thước file Chia/Ghép overallTaskSize, kích thước của part đang thao tác subTaskSize, tên nhiệm vụ TaskName, số part TaskNumber, trước khi bắt đầu Chia/Ghép file trong hàm Split/Merge.
            + Cập nhật tổng số byte đã Chia/Ghép được overallProgress, số byte đã Chia/Ghép được của part hiện tại subProgress, chỉ số part đang Chia/Ghép TaskIndex, sau mỗi lần đọc một byte bufrer từ file nguồn ghi xuống file đích trong hàm Split/Merge. Sau đó gọi hàm ShowProgress() với đối số nhập vào là stProgrees chúng ta vừa cập nhật giá trị xong để hiển thị tiến độ.

            - Để hiển thị tiến độ lên dòng lệnh chỉ cần dùng hàm printf()

            - Để cập nhật tiến độ mà không bị dật và hiển thị ngay tại dòng hiện tại chỉ cần:
            + putchar('\r'); // Để về đầu dòng.
            + printf(...); // In bản tiến độ cập nhật ra màn hình.
            + fflush(stdout); // Xóa bộ nhớ đệm của thiết bị xuất chuẩn để chống dật.

            - Code cụ thể trong hàm ShowProgress(stProgress*) với đối số là một struct stProgress được cấp phát động.

            PHP Code:
            // Show progress bar in terminal
            voidShowProgress stProgressp)
            {
                    
            // Thuộc tính của nó là tĩnh vì cần lưu giá trị của nó trong lần cập nhật tiến độ sau
                
            static int sub_task_percent;
                
            int overall_task_percent 0;
                    
            int new_sub_task_percent 0;

                
            // Tính toán tiến độ thực hiện được theo đơn vị %
                
            if (p->subTaskSize != 0)
                    
            new_sub_task_percent = (int)100 * (p->subProgress) / (p->subTaskSize);

                
            // Cập nhật tiến độ lên stdout nếu tiến độ tăng lên ít nhất 1%
                
            if ( new_sub_task_percent sub_task_percent >= 1)
                {
                    
            sub_task_percent new_sub_task_percent;
                    
            char buffer1[101];
                    
            char buffer2[101];overall_task_percent = (int)100 * (p->overallProgress) / (p->overallTaskSize);

                    
            // Show progress at terminal

                    
            putchar('\r');

                    
            sprintf(buffer1"%s: %3d/%3d Progress: [",p->TaskNamep->TaskIndexp->TaskNumber);

                    
            int i;
                    
            //print '=' char for task complete
                    
            for (1i<= sub_task_percent/i++)
                    {
                        
            strcat(buffer1"=");
                    }

                    if (
            sub_task_percent 100)
                    {
                        if (
            sub_task_percent == 1)
                        {
                            
            strcat(buffer1"-");
                        }
                        else
                        {
                            
            strcat(buffer1">");
                        }
                    }
                    else
                    {
                        
            strcat(buffer1"=");

                    }

                    
            // print space
                    
            for (1i<= 50 sub_task_percent/2i++)
                        
            strcat(buffer1" ");

                    
            // print end message
                    
            if (overall_task_percent 100)
                    {
                        if (
            sub_task_percent 100)
                        {
                            
            sprintf(buffer2"] %3d% | %3d%"sub_task_percentoverall_task_percent);
                        }
                        else
                        {
                            
            sprintf(buffer2"] %3d% | %3d%\n"sub_task_percentoverall_task_percent);
                            
            sub_task_percent 0;
                        }
                    }
                    else
                    {
                            
            sprintf(buffer2"] %3d% | Done\n"sub_task_percent);
                    }

                    
            fprintf(stdout"%s%s"buffer1buffer2);

                    
            fflush(stdout);


                }

                return 
            NULL;

            V - DÙNG THREAD ĐỂ HIỆN TIẾN ĐỘ THỰC HIỆN

            1. Lý do - và nguyên lý:

            Các bạn để ý là mình gọi hàm ShowProgress sau mỗi lần đọc/ghi một byte buffer nên nếu file có kích thước 1 triệu byte thì dĩ nhiên mình phải kiểm tra tiến độ 1 triệu lần trong đó có những lần vô ích (lần kiểm tra tiến độ khi tiến độ chưa tăng lên 1% nào cả).

            Để giải quyết vấn đề này mình tạo một Thread mới chạy hàm ShowProgress(stProgress*) - dĩ nhiên là phải sửa lại một chút rồi để hiển thị tiến độ vì lúc này hàm chỉ được chạy đúng một lần thread chỉ hủy khi Hàm ShowProgress() mới này chạy xong - hàm này chỉ dừng khi Chia/Ghép file xong.

            Trong hàm này mình chỉ cho kiểm tra tiến độ 10 lần trong một giây, với tốc độ này cũng đủ khiến cho bạn cẩm thấy tốc độ của nó rồi, trong thực tế nếu tôc độ đọc ghi của mình là 10MB/s thì hàm ShowProgress cũ phải kiểm tra tiến độ tới 10*1024*1024 lần tốn rất nhiều thời gian và xử lý của CPU lẽ ra giành cho việc đọc ghi thì tốt hơn.

            Nhưng làm sao để thread này có thể kiểm tra tiến độ ?
            Việc đó được thực hiện nhờ vào tham số cấp phát động stProgress* như mình trình bày ở trên, do đó thread mới này có thể tham chiếu đến vũng nhớ động mà giá tri của nó được cập nhật thường xuyên mỗi khi Đọc/Ghi buffer xảy ra trong hàm Merge/Split.
            2. Triển khai:
            - Mình tạo thêm hàm: void CreateThreadProgress (stProgress* Progress) nhưng phải kkhai báo thư viện <pthread.h>
            PHP Code:
            // Create and setting for thread run ShowProgress function
            void CreateThreadProgress (stProgressProgress)
            {
                    
            // Chứa Thread ID
                
            pthread_t thShowProgress;

                    
            // Tạo Thread mới với ID bỏ vào thShowPràoogress, Hàm chạy Thread này là ShowProgress, đối số cho hàm này là con trỏ cấu trúc Progress
                
            pthread_create (&thShowProgressNULL, &ShowProgressProgress);

            Còn hàm ShowProgress() lúc này chuyển thành:

            PHP Code:
            /*
             * merge.c
             *
             *  Created on: Jul 31, 2012
             *      Author: LeAnh
             */

            # include "progress.h"

            // Show progress bar in terminal
            voidShowProgress voidprogress)
            {
                
            char buffer1[101];
                
            char buffer2[101];
                
            char buffer3[101];
                
            int i;

                
            int overall_task_percent 0;
                
            int sub_task_percent 0;
                
            double speed 0;                     // Bytes/second (B/s)
                
            unsigned long remain_time 0// second

                
            stProgress= (stProgress*)progress;
                
            time_t startnow;
                
            start time (0);


                while(
            overall_task_percent 100)
                {
                    
            // Compute speed read/write B/s
                    
            now time (0);
                    
            speed p->overallProgress 1.0 / (now start);
                    
            remain_time = (unsigned long)((p->overallTaskSize p->overallProgress) / speed);

                    
            // Cancel report progress when speed == 0
                    
            if (speed == 0)
                      continue;
                    
                    
            // Compute new progress percent
                    
            if (p->subTaskSize != 0)
                        
            sub_task_percent = (int)100 * (p->subProgress) / (p->subTaskSize);

                    
            overall_task_percent = (int)100 * (p->overallProgress) / (p->overallTaskSize);

                    
            // Show progress at terminal

                    
            sprintf(buffer1"%s: %3d/%-3d Progress: [",p->TaskNamep->TaskIndexp->TaskNumber);

                    
            //print '=' character for task complete
                    
            for (1i<= sub_task_percent/i++)
                    {
                        
            strcat(buffer1"=");
                    }

                    if (
            sub_task_percent 100)
                    {
                        if (
            sub_task_percent == 1)
                        {
                            
            strcat(buffer1"-");
                        }
                        else
                        {
                            
            strcat(buffer1">");
                        }
                    }
                    else
                    {
                        
            strcat(buffer1"=");

                    }

                    
            // print space
                    
            for (1i<= 50 sub_task_percent/2i++)
                        
            strcat(buffer1" ");

                    
            // print end message
                    
            if (overall_task_percent 100)
                    {
                        
            // print speed
                        
            if (speed 1024)
                          
            sprintf(buffer2"] %3d% | %3d% | %7.2f  B/s"sub_task_percentoverall_task_percentspeed);
                        else
                          if (
            speed 1024 1024)
                            
            sprintf(buffer2"] %3d% | %3d% | %7.2f KB/s"sub_task_percentoverall_task_percentspeed/1024);
                          else
                            
            sprintf(buffer2"] %3d% | %3d% | %7.2f MB/s"sub_task_percentoverall_task_percentspeed/1024/1024);

                        
            // print remain time
                        
            if (remain_time 60)
                          
            sprintf(buffer3"%s | %02d s                  "buffer2remain_time);
                        else
                          if (
            remain_time 3600)
                          {
                              if (
            remain_time 60 == 0)
                                
            sprintf(buffer3"%s | %02d minutes            "buffer2remain_time 60);
                              else
                                
            sprintf(buffer3"%s | %02d minutes, %02d seconds"buffer2remain_time 60remain_time 60);
                          }
                    }
                    else
                    {
                        
            sprintf(buffer3"] 100% | Done                                           \n"sub_task_percent);
                    }

                    
            putchar('\r');
                    
            fprintf(stdout"%s%s"buffer1buffer3);

                    
            fflush(stdout);

                    
            // Sleep SLEEP_TIME miliseconds = SLEEP_TIME * 1000 microseconds
                    
            usleep(SLEEP_TIME 1000);

                }

                return 
            NULL;
            }

            // Create and setting for thread run ShowProgress function
            pthread_t CreateThreadProgress (stProgressProgress)
            {
                
            pthread_t thShowProgress;

                
            pthread_create(&thShowProgressNULL, &ShowProgressProgress);
                
                return 
            thShowProgress;

            Dĩ nhiên để dùng nó bạn chỉ cần # include "progress.h" và gọi hàm CreateThreadProgress (_Progress); sau khi cấp phát thành công bộ nhớ cho biến stProgress* _Progress; và hủy việc gọi hàm ShowProgress() trong hàm Split/Merge đi vì hàm mới này được nạp cho một thread khác chạy rồi.

            Lúc này khi hàm Merge/Split chạy nó sẽ khởi động Thread mới này chạy theo và giúp báo tiên độ một cách độc lập.

            Có một vấn đề nảy sinh là nếu như việc Chia/Ghép file diễn ra gần xong rồi (cỡ 99%) nhưng đúng lúc này theo chu kỳ hàm ShowProgress() ngưng làm việc(sleep) và khi nó thức dậy thì ôi thôi Chia/Ghép file xong mất và thoát luôn chương trình chính và đồng nghĩa là nó cũng bị hủy theo trước khi xuất kết quả là 100% --> nhu vậy thật là khó coi phải không ?


            Giải pháp cho nó là hàm pthread_join (pthread_t threadID, void* ReturnValue); nó sẽ khiến chương trình chính phải đợi thread Progress thức dậy xuất ra tiến độ 100% rồi mới kết thúc.
            Để đơn giản mình sửa hàm CreateThreadProgress() một chút để nó return lại threadID của thread chạy hàm ShowProgress để có thể gọi pthread_join trong hàm Merge/Split.
            Hàm CreatThreadProgress() mới như sau:

            PHP Code:
            // Create and setting for thread run ShowProgress function
            pthread_t CreateThreadProgress (stProgressProgress)
            {
                
            pthread_t thShowProgress;
                
            pthread_create (&thShowProgressNULL, &ShowProgressProgress);
                return 
            thShowProgress;

            Trong hàm Merge/Split bạn thêm đoạn mã:
            PHP Code:
            // Create point to Progress struct dynamic allocation and thread to run ShowProgress
                
            stProgress_Progress = (stProgress*)malloc(sizeof(stProgress));
                
            pthread_t thSPID CreateThreadProgress (_Progress); 
            để lấy thread ID là thSPID chuyển cho hàm pthread_join (thSPID, NULL);
            Bây giờ bạn copy hàm này bỏ vào phần trước câu lệnh thông báo Split/Merge hoàn thành là được !


            VI - CẤU TRÚC CÁC FILE CHƯƠNG TRÌNH - CÁCH BIÊN DỊCH VÀ SỬ DỤNG - LINK DOWNLOAD SOURCE CODE CỦA CÁC PHIÊN BẢN:

            1. Cấu trúc fie chương trình:

            Nãy giờ nói về cách hoạt động của chương trình nhiều rồi không có code đầy đủ để test.

            Code của mình có 3 bản:

            MSFVS - Merge & Split file Very Simple nói đơn giản là vì tất cả các hàm mình bỏ trong một file MSF.c không chia thư viện hay file *.h gì cả.
            Còn MSF_Not_USE_THREAD, MSF_USE_THREAD một cái hiển thị progress không dùng thread một cái dùng các bạn giải nén ra biên dịch chạy thử và so sánh rồi cho ý kiến nha !

            Cái MSFVS chỉ có một file nên không có gì để nói, còn MSF_Not_USE_THREAD, MSF_USE_THREAD thì rõ ràng hơn có chia thành các thu viện hàm rõ ràng và cấu trúc tương tự nhau:
            bao gồm: MSF.c; agent.h; agent.c; split.h; split.c; merge.h; merge.c; progress.h; progress.c; trong đó:

            File MSF.c chứa hàm main ().
            File agent.h là đại lý khai báo(cung cấp) các thu viện, các enum, const, struct và các định nghĩa hàm dịch vụ mà các file khác cần dùng đến.
            File agent.c hiện thực hóa các hàm khai báo trong agent.h
            File split.h khai báo các thư viện cần dung kể cả "agent.h" và hàm Split file
            File split.c cụ thể hóa hàm Split ()
            File merge.h khai báo các thư viện cần dung kể cả "agent.h" và hàm Split file
            File merge.c cụ thể hóa hàm Merget ()
            File progress.h khai báo các thư viện cần dùng và định nghĩa struct stProgress, định nghĩa hàm ShowProgress, riêng trong MSF_USE_THREAD có thêm hàm CreateThreadProgress
            File progress.c cụ thể hóa hàm ShowProgress (), riêng trong MSF_USE_THREAD có thêm hàm CreateThreadProgress

            2. Cách biên dịch và thử nghiệm:
            Để chương trình có thể thao tác với các file cỡ lớn > 2GB trở lên bạn phải thêm option -D_FILE_OFSET_BITS=64 khi bên dịch.
            Để chương trình có thể thao tác với Thread bạn phải thêm option -lpthread khi biên dịch.

            Cụ thể: cd vào thư mục /src

            Với MSFVS:
            PHP Code:
            gcc -D_FILE_OFFSET_BITS=64 -c MSF.c
            gcc MSF.-o MSF 

            Với MSF_Not_USE_THREAD:
            PHP Code:
            gcc -D_FILE_OFFSET_BITS=64 *.*.-o MSF 
            Với MSF_USE_THREAD:
            PHP Code:
            gcc -D_FILE_OFFSET_BITS=64 -lpthread *.*.-o MSF 
            Để sử dụng các bạn dùng:
            PHP Code:
            $ ./MSF -
            Để biết thêm chi tiết.

            VD: mình có file: /media/MEDIA/Video/file.mp4 cần chia làm 10 part, lưu tại thư mục /media/MEDIA/ có mật khẩu là greendream muốn xóa file nguồn sau khi hoàn tất các bạn làm như sau:

            PHP Code:
            $ ./MSF -/media/MEDIA/Video/file.mp4 -q 10 -/media/MEDIA/ -p greendream -
            Kết quả như sau:
            [LeAnh@LeAnh src]$ ./MSF -s /media/MEDIA/Video/file.mp4 -q 10 -o /media/MEDIA/ -p greendream -d
            Splitting...
            Creatting part: 1/ 10 Progress: [================================================== =] 100% | 9%
            Creatting part: 2/ 10 Progress: [================================================== =] 100% | 19%
            Creatting part: 3/ 10 Progress: [================================================== =] 100% | 29%
            Creatting part: 4/ 10 Progress: [================================================== =] 100% | 39%
            Creatting part: 5/ 10 Progress: [================================================== =] 100% | 49%
            Creatting part: 6/ 10 Progress: [================================================== =] 100% | 59%
            Creatting part: 7/ 10 Progress: [================================================== =] 100% | 69%
            Creatting part: 8/ 10 Progress: [================================================== =] 100% | 79%
            Creatting part: 9/ 10 Progress: [================================================== =] 100% | 89%
            Creatting part: 10/ 10 Progress: [================================================== =] 100% | Done

            Split file: /media/MEDIA/Video/file.mp4 to 10 part Completed !
            Password: greendream

            Time elapsed is: 0: 1:35
            Để nối lại và xóa các file *.1, *.2,... đi luôn bạn làm như sau :

            PHP Code:
            $ ./MSF -/media/MEDIA/file.mp4.3 -/media/MEDIA/ -p greendream -
            Kết quả như sau:
            [LeAnh@LeAnh src]$ ./MSF -m /media/MEDIA/file.mp4.3 -o /media/MEDIA/ -p greendream -d
            Merging...
            Merging part: 1/ 10 Progress: [================================================== =] 100% | 9%
            Removed file: /media/MEDIA/file.mp4.1 !
            Merging part: 2/ 10 Progress: [================================================== =] 100% | 19%
            Removed file: /media/MEDIA/file.mp4.2 !
            Merging part: 3/ 10 Progress: [================================================== =] 100% | 29%
            Removed file: /media/MEDIA/file.mp4.3 !
            Merging part: 4/ 10 Progress: [================================================== =] 100% | 39%
            Removed file: /media/MEDIA/file.mp4.4 !
            Merging part: 5/ 10 Progress: [================================================== =] 100% | 49%
            Removed file: /media/MEDIA/file.mp4.5 !
            Merging part: 6/ 10 Progress: [================================================== =] 100% | 59%
            Removed file: /media/MEDIA/file.mp4.6 !
            Merging part: 7/ 10 Progress: [================================================== =] 100% | 69%
            Removed file: /media/MEDIA/file.mp4.7 !
            Merging part: 8/ 10 Progress: [================================================== =] 100% | 79%
            Removed file: /media/MEDIA/file.mp4.8 !
            Merging part: 9/ 10 Progress: [================================================== =] 100% | 89%
            Removed file: /media/MEDIA/file.mp4.9 !
            Merging part: 10/ 10 Progress: [================================================== =] 100% | Done
            Removed file: /media/MEDIA/file.mp4.10 !

            Merged 10 file completed output file: /media/MEDIA/file.mp4

            Time elapsed is: 0: 1:44
            3. Link Download source code
            File đính kèm bên dưới !

            B - NHỮNG ĐIỀU CHƯA LÀM ĐƯỢC:

            1. Chưa tạo được bản cài đặt *.tar.gz để cài đặt trên máy Linux khác.

            2. Chưa biết cách đưa thêm option -D_FILE_OFFSET_BITS=64 và -lpthread vào automake ở máy khác (hệ quả do 1 gây ra).

            ===Up==Up==> Đã hoàn tất cả trong #8
            Attached Files
            Last edited by 10520567; 22-08-2012, 18:34.

            Comment


            • #7
              Ủng Hộ Group này một cái, trong thời gian ngôi chờ đợi DAA (sáng giờ để dứt điểm môn cuối cùng) :aboom:, thấy thằng bạn đang code cái này :brick::salute:
              Nên cũng mạn phép code 1 cái :sexy:

              Thực hiện:
              1. Text Editor: Kate
              2. Compile : gcc
              3. Require : make


              gồm 4 file:
              1. configure
              2. function.h
              3. function.c
              4. main.c


              Thư viện ngoài : không, thay vào đó sử dụng thư viện chuẩn ANSI C và thư viện chuẩn của Linux

              File compile để kiểm tra hệ thống là x86 hay x86_64 để ra file Makefile tương ứng
              - do sẽ compile file function.c ra file thư viện liên kết động nên phải kiểm ra phần arch của hệ thống qua lện `uname -m`, nếu là x86_64 thì PATHLIB là /usr/lib64, hay x86 thì sẽ là /usr/lib
              PHP Code:
              #!/bin/sh

              ARCH=`uname -m`
              if [ 
              $ARCH="x86_64" ]; then
                  cat 
              Makefile <<EOF
              #    Make FIle
              #    author: Peter Nguyen
              #############################

              CC gcc
              BINPATH
              =/usr/bin
              LIBPATH
              =/usr/lib64

              lspit
              liblsplit.so function.h
                   
              \$(CCmain.-o lsplit -L. -llsplit
              liblsplit
              .so: function.o
                  
              \$(CC) -shared function.-o liblsplit.so
              function.o:
                  \$(
              CC) --fPIC function.c

              clean
              :
                  
              rm -rf *.o
                  rm 
              -rf *.so
                  rm 
              -rf lsplit
              install
              lsplit
                  
              @cp -Rv liblsplit.so \$(LIBPATH
                  @
              cp -Rv lsplit \$(BINPATH
              uninstalllsplit
                  
              @echo "Uninstall Lsplit"
                  
              @rm -Rv \$(LIBPATH)/liblsplit.so
                  
              @rm -Rv \$(BINPATH)/lsplit
              EOF
              else
                  
              cat Makefile <<EOF
              #    Make FIle
              #    author: Peter Nguyen
              #############################

              CC=gcc
              BINPATH
              =/usr/bin
              LIBPATH
              =/usr/lib

              lspit
              liblsplit.so function.h
                  
              $(CCmain.-o lsplit -L. -llsplit
              liblsplit
              .so: function.o
                  
              $(CC) -shared function.-o liblsplit.so
              function.o:
                  $(
              CC) --fPIC function.c

              clean
              :
                  
              rm -rf *.o
                  rm 
              -rf *.so
                  rm 
              -rf lsplit
              install
              lsplit
                  
              @cp -Rv liblsplit.so $(LIBPATH
                  @
              cp -Rv lsplit $(BINPATH
              uninstalllsplit
                  
              @echo "Uninstall Lsplit"
                  
              @rm -Rv $(LIBPATH)/liblsplit.so
                  
              @rm -Rv $(BINPATH)/lsplit
              EOF
              fi
              echo "Done." 
              File function.c chứa các hàm xử lý file.
              - ở đây thì giới hạn kích thước mỗi lần đọc hay ghi file là 3MB tránh trình trạng quá tải bộ nhớ nếu load lớn hơn 3MB (vd part file là 100MB load lên bộ nhớ sẽ gây lag):shot:, sử dụng thư viện dirent cho phép đọc một thư mục để tìm ra các file *.part*
              PHP Code:
              #include<stdio.h>
              #include<stdlib.h>
              #include<string.h>
              #include<unistd.h>
              #include<sys/stat.h>
              #include<dirent.h>

              #define MAX 255
              const int MB=1024*1024;

              unsigned long getSizeFile(char *filename){
                  
              unsigned long size;
                  
              FILE *fRead;
                  if(!(
              fRead fopen(filename,"rb"))){
                      return 
              0;
                  }else{
                      
              fseek(fRead,0,SEEK_END);
                      
              size ftell(fRead);
                      return 
              size;
                  }
              }

              void show_Process(char *process_name,unsigned long bytes_write,unsigned long partSize){
                  
              double current_process;
                  
              char buff1[MAX];
                  
              char buff2[MAX];
                  
              int i;
                  
                  
              current_process 0;
                  if(
              current_process 100){
                      
              putchar('\r');
                      
              current_process = (double)bytes_write/(double)partSize*100;
                      
              sprintf(buff1,"--> %s : [",process_name);
                      
                      for(
              1<= (current_process/4); i++){
                          
              strcat(buff1,"#");
                      }
                      
                      for(
              1<= (25-current_process/4); i++){
                          
              strcat(buff1," ");
                      }
                      
                      if(
              current_process 100){
                          
              sprintf(buff2,"] %3.2f\%",current_process);
                      }else{
                          
              sprintf(buff2,"] %3.2f\% | Done.\n",current_process);
                      }
                      
                      
              fprintf(stdout,"%s%s",buff1,buff2);
                      
              fflush(stdout);
                  }
              }

              void split_file(char *filename,char *dirint part){
                  
              unsigned long fileSize getSizeFile(filename);
                  
              unsigned long partSize fileSize/part;
                  
              unsigned long bytes,bytes_sum;
                  
              char *partName;
                  
              char *buffer;
                  
              FILE *fRead;
                  
              FILE *fWrite;
                  
              int i,j;
                  
                  
              fRead fopen(filename,"rb");
                  
                  if(!
              fRead){
                      
              fprintf(stderr,"[!] Error While Openning File %s\n",filename);
                      return;
                  }
                  
                  for(
              1<= part;i++){
                      if(
              partSize > (3*MB)){
                          
              buffer = (char *)malloc(3*MB*sizeof(char));
                      }else{
                          
              buffer = (char *)malloc(partSize*sizeof(char));
                      }
                      
              partName = (char *)malloc(MAX*sizeof(char));
                      if(! 
              dir){
                          
              sprintf(partName,"%s.part%d",filename,i);
                      }else{
                          
              sprintf(partName,"%s%d",dir,i);
                      }
                      
                      if(
              == part){
                          
              partSize fileSize partSize*(i-1);
                      }
                      
                      if(!(
              fWrite fopen(partName,"wb"))){
                          
              fprintf(stderr,"[!] Error While Openning File %s\n",partName);
                          
              fclose(fRead);
                          return;
                      }
                      
                      if(
              partSize 3*MB){
                          
              bytes_sum 0;
                          for(
              1<= (partSize/(3*MB)); j++){
                              if(!(
              fread(buffer,1,3*MB,fRead))){
                                  
              fprintf(stderr,"[!] Error While Reading File %s \n",filename);
                                  
              fclose(fRead);
                                  return;
                              }
                              if(!(
              bytes fwrite(buffer,1,3*MB,fWrite))){
                                  
              fprintf(stderr,"[!] Error While Writting File %s\n",partName);
                                  
              fclose(fRead);
                                  return;
                              }
                              
              bytes_sum+=bytes;
                              if((
              partSize bytes_sum) < (3*MB)){
                                  if(!(
              fread(buffer,1,(partSize bytes_sum),fRead))){
                                      
              fprintf(stderr,"[!] Error While Reading File %s \n",filename);
                                      
              fclose(fRead);
                                      return;
                                  }
                                  if(!(
              bytes fwrite(buffer,1,(partSize bytes_sum),fWrite))){
                                      
              fprintf(stderr,"[!] Error While Writting File %s\n",partName);
                                      
              fclose(fRead);
                                      return;
                                  }
                                  
              bytes_sum +=bytes;
                              }
                              
              show_Process(partName,bytes_sum,partSize);
                          }
                          
              fprintf(stdout,"[#] Total Bytes : %3.2f MB\n",(double)getSizeFile(partName)/(double)MB);
                          
              fflush(stdout);
                      }else{
                          if(!(
              fread(buffer,1,partSize,fRead))){
                              
              fprintf(stderr,"[!] Error While Reading File %s \n",filename);
                              
              fclose(fRead);
                              return;
                          }
                              
                          if(!(
              bytes fwrite(buffer,1,partSize,fWrite))){
                              
              fprintf(stderr,"[!] Error While Writting File %s\n",partName);
                              
              fclose(fRead);
                              return;
                          }
                          
              show_Process(partName,bytes,partSize);
                          
              fprintf(stdout,"Total Bytes : %3.2f MB\n",(double)getSizeFile(partName)/(double)MB);
                      }
                      
              free(buffer);
                      
              free(partName);
                      
              fclose(fWrite);
                  }
                  
              fclose(fRead);
              }

              void getFileMatch(char *pattern,char *dir,int *part,unsigned long *fileSize){
                  
              DIR *dp;
                  
              struct dirent *entry;
                  
              struct stat statbuf;
                  
              char buff[MAX];
                  
              int i;
                  
                  if(!(
              dp opendir(dir))){
                      
              fprintf(stderr,"[!] Error Openning Dir %s \n",dir);
                      return;
                  }
                  
              chdir(dir);
                  while(
              entry readdir(dp)){
                      
              stat(entry->d_name,&statbuf);
                      if(
              S_ISREG(statbuf.st_mode)){
                          if(
              strncmp(pattern,entry->d_name,strlen(pattern)) == 0){
                              (*
              part)++;
                              (*
              fileSize)+=getSizeFile(entry->d_name);
                          }
                      }
                  }
                  
              closedir(dp);
              }

              void merge_file(char *fileout,char *dir,char *pattern){
                  
              FILE *fRead;
                  
              FILE *fWrite;
                  
              int i,partlength=0,j;
                  
              char *buff;
                  
              char filematch[MAX];
                  
              unsigned long partSize,fileSize=0,bytes;
                  
              unsigned long bytes_sum,bytes_sum_part;
                  
                  
              getFileMatch(pattern,dir,&partlength,&fileSize);
                  if(
              partlength){
                      if(
              fWrite fopen(fileout,"wb")){
                          
              bytes_sum=0;
                          for(
              1<= partlengthi++){
                              
              sprintf(filematch,"%s%d",pattern,i);
                              
              partSize getSizeFile(filematch);
                              if(
              partSize > (3*MB)){
                                  
              buff = (char *)malloc(3*MB*sizeof(char));
                              }else{
                                  
              buff = (char *)malloc(partSize*sizeof(char));
                              }
                              if(!(
              fRead fopen(filematch,"rb"))){
                                  
              fprintf(stderr,"[!] Error Open File %s\n",filematch);
                                  return;
                              }
                              if(
              partSize > (3*MB)){
                                  
              bytes_sum_part=0;
                                  for(
              1<= (partSize/(3*MB)); j++){
                                      if(!
              fread(buff,1,3*MB,fRead)){
                                          
              fprintf(stderr,"[!] Error While Reading File %s\n",filematch);
                                          
              fclose(fWrite);
                                          
              fclose(fRead);
                                          return;
                                      }
                                      if(!(
              bytes fwrite(buff,1,3*MB,fWrite))){
                                          
              fprintf(stderr,"[!] Error While Writting File %s\n",fileout);
                                          
              fclose(fWrite);
                                          
              fclose(fRead);
                                          return;
                                      }
                                      
              bytes_sum_part+=bytes;
                                      if((
              partSize bytes_sum_part) < (3*MB)){
                                          if(!
              fread(buff,1,(partSize bytes_sum_part),fRead)){
                                              
              fprintf(stderr,"[!] Error While Reading File %s\n",filematch);
                                              
              fclose(fWrite);
                                              
              fclose(fRead);
                                              return;
                                          }
                                          if(!(
              bytes =fwrite(buff,1,(partSize bytes_sum_part),fWrite))){
                                              
              fprintf(stderr,"[!] Error While Writting File %s\n",fileout);
                                              
              fclose(fWrite);
                                              
              fclose(fRead);
                                              return;
                                          }
                                          
              bytes_sum_part+=bytes;
                                      }
                                      
                                  }
                                  
              bytes_sum+=bytes_sum_part;
                                  
              show_Process(fileout,bytes_sum,fileSize);
                              }else{
                                  if(!
              fread(buff,1,partSize,fRead)){
                                      
              fprintf(stderr,"[!] Error While Reading File %s\n",filematch);
                                      
              fclose(fWrite);
                                      
              fclose(fRead);
                                      return;
                                  }
                                  
              bytes fwrite(buff,1,partSize,fWrite);
                                  if(!
              bytes){
                                      
              fprintf(stderr,"[!] Error While Writting File %s\n",fileout);
                                      
              fclose(fWrite);
                                      
              fclose(fRead);
                                      return;
                                  }
                                  
              bytes_sum+=bytes;
                                  
              show_Process(fileout,bytes_sum,fileSize);
                              }
                              
              free(buff);
                              
              fclose(fRead);
                          }
                          
              fprintf(stdout,"[*] Total Bytes : %3.2f MB\n",(double)getSizeFile(fileout)/(double)MB);
                      }else{
                          
              fprintf(stderr,"[!] Error Can't Open File %s\n",fileout);
                          return;
                      }
                      
              fclose(fWrite);
                  }else{
                      
              fputs("[!] Error No File Was Founded\n",stderr);
                  }
                  
              fprintf(stdout,"[*] File Was Save In %s\n",fileout);

              file function.h lưu các hàm trong function.c để sử dụng vào main.c
              PHP Code:
              #define MAX 255

              unsigned long getSizeFile(char *filename);
              void split_file(char *filename,char *dirint part);
              void merge_file(char *fileout,char *dir,char *pattern_in);
              void getFileMatch(char *pattern,char *dir,int *part,unsigned long *fileSize); 
              main.c chứa các xử lý trên đối số dòng lệnh vào
              PHP Code:
              #include <stdio.h>
              #include <stdlib.h>
              #include <getopt.h>
              #include <unistd.h>
              #include "function.h"

              const char *__version__ "0.1";
              const 
              char *__author__ "PeterNguyen";

              void Help();
              void Version();

              int main (int argcchar **argv){
                  
              int c;
                  
              int part=0;
                  
              int flag;
                  
              char *filename=NULL,*fileout=NULL;
                  
              char *dir=NULL;
                  while (
              1){
                      static 
              struct option long_options[] ={
                          
              /* These options set a flag. */
                          
              {"split",required_argument,0,'s'},
                          {
              "merge",required_argument,0,'m'},
                          {
              "output",required_argument,0,'o'},
                          {
              "part",required_argument,0,'p'},
                          {
              "dir",required_argument,0,'d'},
                          {
              "help",no_argument,0,'h'},
                          {
              "version",no_argument,0,'v'},
                          {
              0000}
                      };
                      
              /* getopt_long stores the option index here. */
                      
              int option_index 0;
                      
                      
              getopt_long (argcargv"s:p:d:o:m:vh",long_options, &option_index);
                      if (
              == -1)
                          break;
                   
                      switch (
              c){
                          case 
              0:
                              
              fprintf(stderr,"Usage %s -h to help",argv[0]);
                              return 
              0;
                          case 
              's':
                              if(
              optarg){
                                  
              fprintf(stdout,"[+] Start Split File: %s\n",optarg);
                                  
              filename optarg;
                                  
              flag='s';
                              }else{
                                  
              fputs("[!] No File To Split\n",stderr);
                                  
              fprintf(stderr,"[*] Usage %s -s <file to split> -p part",argv[0]);
                                  return 
              0;
                              }
                              break;
                          case 
              'p':
                              
              part atoi(optarg);
                              break;
                          case 
              'o':
                              
              fileout optarg;
                              break;
                          case 
              'd':
                              
              dir optarg;
                              break;
                          case 
              'm':
                              if(
              optarg){
                                  
              fprintf(stdout,"[+] Start Merge File: %s\n",optarg);
                                  
              flag='m';
                                  
              filename optarg;
                              }else{
                                  
              fputs("[!] No File To Merge\n",stderr);
                                  
              fprintf(stderr,"[*] Usage %s -m <file to join> \n",argv[0]);
                                  return 
              0;
                              }
                              break;
                          case 
              'h':
                              
              Help();
                              break;
                          case 
              'v':
                              
              Version();
                              break;
                          default:
                              
              fprintf(stderr,"Usage %s -h to help\n",argv[0]);
                              break;
                      }
                  }
                  switch(
              flag){
                      case 
              's':
                          if(
              part && filename){
                              
              split_file(filename,dir,part);
                          }else{
                              
              fprintf(stderr,"[*] Usage %s -s <file to split> -p part\n",argv[0]);
                          }
                          break;
                      case 
              'm':
                          if(
              filename && fileout){
                              if (
              dir == NULL){
                                  
              dir = (char *)malloc(2048*sizeof(char));
                                  
              getcwd(dir,2048);
                              }
                              
              merge_file(fileout,dir,filename);
                          }else{
                              
              fprintf(stderr,"[*] Usage %s -m <file to join> -o fileout\n",argv[0]);
                          }
                          break;
                  } 
                  return 
              0;
              }

              void Version(){
                  
              fprintf(stdout,"Version: %s - Author: %s\n",__version__,__author__);
              }

              void Help(){
                  
              fputs("\t--> -m : merge file <filename>\n",stdout);
                  
              fputs("\t\t lsplit (-d <your dir>) -m <first part> -o <link to save>\n",stdout);
                  
              fputs("\t--> -s : split file <filename>\n",stdout);
                  
              fputs("\t\t lsplit -s <pattern> -p <part> -o (-d <link to save part file>)\n",stdout);
                  
              fputs("\t\t\t file split has .part1,2,3.. type <name>.part = <parttern>\n",stdout);
                  
              fputs("\t--> -p : part to split <interge>\n",stdout);
                  
              fputs("\t--> -o : path/to/fileout <link> \n",stdout);
                  
              fputs("\t--> -d : direct to open part file\n",stdout);
                  
              fputs("\t--> -l : licences\n",stdout);
                  
              fputs("\t--> -h : help\n",stdout);

              Biên dịch:
              Code:
              ./configure
              make
              sudo make install
              Cấu hình trên máy Core i5 (đời 2) Ram 4Gb
              • Tốc độ chia file 1.3GB thành 6 part là 1 phút 34 s
              • Tốc độ hợp nhât file từ 6 part là 1 phút 47s

              Code:
              [peternguyen@Peter-Nguyen ~]$ lsplit -s ancient.aliens.s04e01.mkv -p 6
              [+] Start Split File: ancient.aliens.s04e01.mkv
              --> ancient.aliens.s04e01.mkv.part1 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              --> ancient.aliens.s04e01.mkv.part2 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              --> ancient.aliens.s04e01.mkv.part3 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              --> ancient.aliens.s04e01.mkv.part4 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              --> ancient.aliens.s04e01.mkv.part5 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              --> ancient.aliens.s04e01.mkv.part6 : [#########################] 100.00% | Done.
              [#] Total Bytes : 226.34 MB
              Code:
              [peternguyen@Peter-Nguyen ~]$ lsplit -m ancient.aliens.s04e01.mkv.part -o ancient.alients.s04e01.mkv
              [+] Start Merge File: ancient.aliens.s04e01.mkv.part
              --> ancient.alients.s04e01.mkv : [#########################] 100.00% | Done.[*] Total Bytes : 1358.04 MB[*] File Was Save In ancient.alients.s04e01.mkv
              Attached Files
              Last edited by 11520118; 15-08-2012, 13:34.

              Comment


              • #8
                Hoan nghênh tinh thần của " Hoàng ". Rất vui khi bạn tham gia cùng CLUB OSS-LUG sinh hoạt vào 08:00 thứ 6 hàng tuần tại phòng CTTT (cạnh PĐT)

                Về chương trình của bạn thì khá tốt nhưng còn hơi ít tùy chọn: như tùy chọn kích thước mỗi part, xóa file sau khi Merge/Split xong, đặt mật khẩu mã hóa file... Có một điểm quan trọng là bạn gây thất thoát bộ nhớ lúc cần chỉnh lại kích thước của Buffer. Nhưng đặc biệt nhất là file scrip cofigure của bạn khiến tôi khá bất ngờ, không biết bạn viết tay hay dùng công cụ nào.
                Nhưng nó không chạy đc trên máy của tôi:
                [root@LeAnh lsplit]# make install
                `liblsplit.so' -> `/usr/lib64'
                `lsplit' -> `/usr/bin/lsplit'
                [root@LeAnh lsplit]# lsplit --help
                lsplit: error while loading shared libraries: liblsplit.so: cannot open shared object file: No such file or directory
                Nó báo không nạp được các thư viện liên kết động *.so và có lẽ phải sửa scrip để nó copy các file *.so bỏ vào usr/lib/ thì mới chạy trên máy 32 bits được cái của bạn là usr/lib64/ cho máy 64 bits

                Khi chương trình còn đơn giản thì bạn có thể làm bằng tay nhưng sau này nó phức tạp lên e rằng làm hơi cực !


                Như ở trên anh Chánh đã giới thiệu bộ công cụ GNU autotool rất tiện lợi giúp bạn tạo một gói cài đặt theo đúng chuẩn và khá nhanh gọn

                Sau đây tôi sẽ trình bày tóm lược lại chương trình của tôi, một số cải tiến, và cách tạo gói cài đặt:

                + So với phiên bản trước ở trên do chưa quan tâm đến tốc độ nên thuật toán của tôi khá đơn giản chỉ đọc/ghi từng byte một từ file nguồn xuống file đích nên tốn rất nhiều thời gian xử lý của CPU cũng như số lần chuyển vị trí đầu đọc/ghi của ổ cứng --> tốn thời gian không đáng có.

                + Giải pháp cũng như Hoàng đã nêu đó là tăng kích thước Buffer lên sẽ nhưng không chỉ giới hạn ở 3MB như của Hùng, ở chương trình của tôi người dùng có thể nhập kích thước Buffer tùy vào cấu hình và tình trạng của máy với tùy chọn -b <positive integer unit is MB> nhưng mặc định nó là 10 MB máy vẫn chạy ổn định !

                + Ngoải ra để báo tiến độ tôi dùng một Thread chạy hàm ShowProgress(stProgress *_Progress) nó sẽ báo tiến độ cho người dùng 10 lần một giây để giảm số lần tính toán tiến độ

                Về phương pháp đọc ghi với Buffer có kích thước tùy ý có thể trình bày ngắn gọn ý tưởng như sau:
                - Xác định kích thước Buffer và cấp phát cho Buffer
                + Nếu kích thước part_size < Buffer_size thì cho Buffer_size = part size ngược lại Buffer_size = DEFAULT_BUFER_SIZE
                + Trong lần đọc/ghi cuối của mỗi part nếu phần còn lại cần đọc/ghi nhỏ hơn Buffer_size thì phải realloc() Buffer với kích thước bằng phần dư còn lại.
                - Lưu ý khi kích thước Buffer thay đổi phải cấp phát lại thì bạn hãy dùng realloc() thay vì malloc nó sẽ xin HĐH cấp một khối nhớ mới cho Buffer trỏ tới và vùng nhớ cũ do Buffer quản lý sẽ mất kiểm soát do không có con trỏ nào quản lý cả.


                - Vậy là xác định được kích thước Buffer còn về báo tiến độ, cái này gần như tách biệt với việc Merge/Split file. Muốn báo tiến độ thì sau khi đọc/ghi xong một buffer bạn phải cập nhật lại giá trị của con trỏ cấu trúc stProgress để chuyền cho Thread ShowProgress xử lý báo Tiến độ, Tốc độ, Thời gian còn lại.
                PHP Code:
                // Define structure progress
                typedef struct {
                    
                unsigned long long    overallTaskSize;
                    
                unsigned long long    subTaskSize;
                    
                double                overallProgress;
                    
                double                subProgress;
                    
                char*                TaskName;
                    
                int                    TaskNumber;
                    
                int                    TaskIndex;
                stProgress
                Lưu ý là kích thước Buffer càng nhỏ thì việc tính Tốc độ và Thời gian còn lại càng chính xác !

                Còn về tạo file cài đặt dùng GNU autotool mình xin tóm tắt các bước như sau:

                0. Chuẩn bị công cụ như của anh Chánh:

                Trên Ubuntu:
                PHP Code:
                sudo apt-get install automake autoconf libtool 
                Trên Fedora:
                PHP Code:
                sudo yum install automake autoconf libtool 
                1. Tạo thư mục rỗng tên MSF hay gì đó tùy bạn và thư mục con của nó src/ và copy toàn bộ mã nguồn vào MSF/src/:
                Nếu chương trình của bạn gồm các file dạng như sau:

                src/MSF.c
                src/agent.h src/agent.c
                src/split.h src/split.c
                src/merge.h src/merge.c
                src/progress.h src/progress.c
                Để đảm bảo thành công các bạn có thể làm tương tự như các bước tiếp theo:

                2. Tạo file MSF/Makefile.am có nội dung tương tự như sau:
                PHP Code:
                bin_PROGRAMS MSF
                MSF_SOURCES 
                src/MSF.c src/agent.h src/agent.c src/split.h src/split.c src/merge.h src/merge.c src/progress.h src/progress.
                3. Tạo file MSF/configure.ac bằng lệnh:
                PHP Code:
                autoscan 
                Để có file MSF/configure.scan bạn đổi tên thành MSF/configure.ac và chỉnh sửa nội dung, kết quả như sau:

                Code:
                #                                               -*- Autoconf -*-
                # Process this file with autoconf to produce a configure script.
                
                AC_PREREQ([2.68])
                [COLOR="#0000CD"][B]AC_INIT([Merge & Split File], [0.1], [greendream.ait@gmail.com])[/B][/COLOR]
                AC_CONFIG_SRCDIR([config.h.in])
                AC_CONFIG_HEADERS([config.h])
                [COLOR="#0000CD"][B]AM_INIT_AUTOMAKE[/B][/COLOR]
                
                # Checks for programs.
                AC_PROG_CC
                [COLOR="#0000CD"][B]AM_PROG_CC_C_O[/B][/COLOR]
                
                # Checks for libraries.
                
                # Checks for header files.
                AC_CHECK_HEADERS([stdlib.h string.h])
                
                # Checks for typedefs, structures, and compiler characteristics.
                
                # Checks for library functions.
                AC_FUNC_MALLOC
                AC_FUNC_REALLOC
                
                AC_CONFIG_FILES([Makefile])
                AC_OUTPUT
                Những chỗ đánh dấu là chỗ bạn phải thêm và thay đổi cho phù hợp.
                còn lại là phần tạo tự động.

                4. Nhưng để chương trình xử lý được file > 2GB bạn phải thêm được option -D_FILE_OFFSET_BITS=64 cho gcc lúc người dùng gọi lênh $ make, ngoài ra vì chương trình của tôi dùng thread nên bạn phải thêm 2 option nữa là -lpthread-pthread còn để làm được như vậy, đơn giản là thêm dòng sau :
                MSF_CFLAGS = -D_FILE_OFFSET_BITS=64 -lpthread -pthread
                vào file MSF/Makefile.am xong nó có nội dung như sau:

                PHP Code:
                bin_PROGRAMS MSF
                MSF_SOURCES 
                src/MSF.c src/agent.h src/agent.c src/split.h src/split.c src/merge.h src/merge.c src/progress.h src/progress.c
                MSF_CFLAGS 
                = -D_FILE_OFFSET_BITS=64 -lpthread -pthread 
                5. Tạo các file cần thiết để biên dich tự động các bạn chạy lần lượt các lệnh sau:
                PHP Code:
                touch NEWS README AUTHORS ChangeLog   # Tạo các file thông tin cần thiết về phần mềm tác giả...
                aclocal
                autoconf
                autoheader
                automake -a
                $./configure 
                Bạn có thể quan sát kết quả sau mỗi lệnh nếu là cảnh báo thì không đáng lo ngại cứ tiếp tục.

                6. Để thử nghiệm có thành công hay ko bạn chạy lệnh sau:
                PHP Code:
                make
                sudo make install
                MSF --help 
                Để cài đặt tự động trên chính máy mình file thực thi sẽ nằm tại thư mục /usr/local/bin hoặc /usr/bin/ tùy phiên bản Kernel
                và xem hướng dẫn bằng tùy chọn --help hoặc -h như trên.

                7. Cuối cùng là cho sản phẩm ra lò phân phối cho người khác bằng lệnh:
                PHP Code:
                make dist 
                nó sẽ tạo gom tất cả các file, thư mục cần thiết để tạo đươc một gói cài đặt hoàn chỉnh dạng *.tar.gz ngay trong thư mục MSF/

                OK thế là xong !
                Dưới đây là file sản phẩm đính kềm theo phiên bản 0.1


                8. Yêu cầu thêm cho buổi sinh hoạt tới là VIẾT GIAO DIỆN CHO CHƯƠNG TRÌNH MERGE & SPLIT FILE

                Quên mất, để cài đặt thử nghiệm bạn tải file đính kèm merge---split-file-0.1.tar.gz giải nén rồi cài đặt như sau:
                PHP Code:
                cd     merge---split-file-0.1
                $ ./configure
                make
                sudo make install 
                Rồi nhập root password nhấn Enter thế là xong và bạn có thể Merge/Split file như trong phần cuối #6 bên trên chỉ khác là thay vì phải ./MSF tại thư mục chứa file thực thi bạn chỉ cần gọi MSF [OPTION] ..... ở bất cứ đâu !
                Good luck !
                Attached Files
                Last edited by 10520567; 02-11-2012, 23:44.

                Comment


                • #9
                  Originally posted by 10520567 View Post
                  Hoan nghênh tinh thần của " Nguyễn Vũ Hoàng "
                  Rất vui khi bạn tham gia cùng CLUB OSS-LUG sinh hoạt vào 08:00 thứ 6 hàng tuần tại phòng CTTT (cạnh PĐT)


                  Về chương trình của bạn thì khá tốt nhưng còn hơi ít tùy chọn: như tùy chọn kích thước mỗi part, xóa file sau khi Merge/Split xong, đặt mật khẩu mã hóa file...
                  Nhưng đặc biệt nhất scrip cofigure của bạn khiến tôi khá bất ngờ, không biết bạn viết tay hay dùng công cụ nào.
                  Nhưng nó không chạy đc trên máy của tôi:
                  => nó ko chạy được là do em gõ nhầm =)) makefile và configure là code bằng tay =))

                  thự ra thì code mình paste nó chạy đc trên 32 bit code mình đính kèm là mình chưa sửa copy paste. Cái file ./configure đơn giản à:
                  đại khái
                  nếu uname -m là x86_64 thì PATHLIB là /usr/lib64 và ngược lại . Do code cái make file trước, sau đó mới làm ./configure nên copy paste có lỗi :misdoubt::sweat:
                  PHP Code:
                  #!/bin/sh

                  ARCH=`uname -m`
                  if [ 
                  $ARCH="x86_64" ]; then
                      cat 
                  Makefile <<EOF
                  #    Make FIle
                  #    author: Peter Nguyen
                  #############################

                  CC gcc
                  BINPATH
                  =/usr/bin
                  LIBPATH
                  =/usr/lib64

                  lspit
                  liblsplit.so function.h
                       
                  $(CCmain.-o lsplit -L. -llsplit
                  liblsplit
                  .so: function.o
                      
                  $(CC) -shared function.-o liblsplit.so
                  function.o:
                      $(
                  CC) --fPIC function.c

                  clean
                  :
                      
                  rm -rf *.o
                      rm 
                  -rf *.so
                      rm 
                  -rf lsplit
                  install
                  lsplit
                      
                  @cp -Rv liblsplit.so $(LIBPATH
                      @
                  cp -Rv lsplit $(BINPATH
                  uninstalllsplit
                      
                  @echo "Uninstall Lsplit"
                      
                  @rm -Rv $(LIBPATH)/liblsplit.so
                      
                  @rm -Rv $(BINPATH)/lsplit
                  EOF
                  else
                      
                  cat Makefile <<EOF
                  #    Make FIle
                  #    author: Peter Nguyen
                  #############################

                  CC=gcc
                  BINPATH
                  =/usr/bin
                  LIBPATH
                  =/usr/lib

                  lspit
                  liblsplit.so function.h
                      
                  $(CCmain.-o lsplit -L. -llsplit
                  liblsplit
                  .so: function.o
                      
                  $(CC) -shared function.-o liblsplit.so
                  function.o:
                      $(
                  CC) --fPIC function.c

                  clean
                  :
                      
                  rm -rf *.o
                      rm 
                  -rf *.so
                      rm 
                  -rf lsplit
                  install
                  lsplit
                      
                  @cp -Rv liblsplit.so $(LIBPATH
                      @
                  cp -Rv lsplit $(BINPATH
                  uninstalllsplit
                      
                  @echo "Uninstall Lsplit"
                      
                  @rm -Rv $(LIBPATH)/liblsplit.so
                      
                  @rm -Rv $(BINPATH)/lsplit
                  EOF
                  fi
                  echo "Done." 
                  Last edited by 11520118; 19-08-2012, 20:42.

                  Comment

                  LHQC

                  Collapse
                  Working...
                  X