# 複製檔案

說明：這個程式的目的是示範複製檔案的功能，在程式設計層面可以學習到檔案的處理，基本的流程是開啟檔案、對檔案進行讀取或寫入的動作、最後不再存取檔案時則關閉檔案。

* 開啟檔案（並指定開啟檔案的權限模式）: open()
* 讀取檔案: read()
* 寫入檔案: write()
* 關閉檔案: close()

程式碼路徑：tlpi-dist/fileio/copy.c

{% code title="Listing 4-1: copy.c" lineNumbers="true" %}

```c
/*************************************************************************\
*                  Copyright (C) Michael Kerrisk, 2022.                   *
*                                                                         *
* This program is free software. You may use, modify, and redistribute it *
* under the terms of the GNU General Public License as published by the   *
* Free Software Foundation, either version 3 or (at your option) any      *
* later version. This program is distributed without any warranty.  See   *
* the file COPYING.gpl-v3 for details.                                    *
\*************************************************************************/

/* Listing 4-1 */

/* copy.c 
   將 argv[1] 指名的檔案複製到 argv[2] 指名的新檔案
*/
#include <sys/stat.h>
#include <fcntl.h>
#include "tlpi_hdr.h"

#ifndef BUF_SIZE        /* 在 cc 編譯時，可以帶入編譯參數，例如：使用 "-D BUF_SIZE=512" 的方式來以 512 複蓋這裡的定義值 1024 */
#define BUF_SIZE 1024
#endif

int
main(int argc, char *argv[])
{
    int inputFd, outputFd, openFlags;
    mode_t filePerms;
    ssize_t numRead;
    char buf[BUF_SIZE];

    if (argc != 3 || strcmp(argv[1], "--help") == 0)
        usageErr("%s old-file new-file\n", argv[0]);

    /* 開啟檔案做為讀取輸入與寫入輸出 */

    /* 開啟要複製的檔案來源，以唯讀模式開啟 */
    inputFd = open(argv[1], O_RDONLY);
    if (inputFd == -1)
        errExit("opening file %s", argv[1]);

    openFlags = O_CREAT | O_WRONLY | O_TRUNC;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
                S_IROTH | S_IWOTH;      /* rw-rw-rw- */

    /* 開啟要複製的檔案目的，需要能夠寫入 */                
    outputFd = open(argv[2], openFlags, filePerms);
    if (outputFd == -1)
        errExit("opening file %s", argv[2]);

    /* 持續複製資料，直到遇到檔案結尾或是發生錯誤為止 */
    while ((numRead = read(inputFd, buf, BUF_SIZE)) > 0)
        if (write(outputFd, buf, numRead) != numRead)
            fatal("write() returned error or partial write occurred");
            
    if (numRead == -1)
        errExit("read");

    if (close(inputFd) == -1)
        errExit("close input");
        
    if (close(outputFd) == -1)
        errExit("close output");

    exit(EXIT_SUCCESS);
}
```

{% endcode %}

編譯程式：

```bash
tlpi-dist/fileio$ gcc copy.c ../libtlpi.a -o copy -I ../lib
```

執行程式：測試複製檔案 copy.c 到 copy1.c。

```
$ ./copy copy.c copy1.c
```

如果想在編譯時指定 BUF\_SIZE，可以用 -D 編譯參數定義 BUF\_SIZE 的數值：

```bash
tlpi-dist/fileio$ gcc copy.c ../libtlpi.a -o copy -I ../lib -DBUF_SIZE=2048
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tlpi-zhtw.netdpi.net/tlpi_sample/sample-chap4/sample-list-4-1.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
