# 機能設計書 81-テキスト処理

## 概要

本ドキュメントは、FreeBSDにおけるテキスト処理コマンド群（grep、awk、diff、cut、column等）の機能設計を記述する。これらはPOSIX準拠のテキスト検索・加工・比較ユーティリティであり、FreeBSDのベースシステムに含まれるユーザ空間コマンドである。

### 本機能の処理概要

**業務上の目的・背景**：テキスト処理コマンドはUNIX/BSD系システムの根幹をなすユーティリティ群である。シェルスクリプト、ログ解析、データ加工、設定ファイルの編集支援など、システム管理からソフトウェア開発まで幅広い場面で日常的に使用される。パイプライン処理と組み合わせることで複雑なテキスト変換を実現でき、UNIX哲学の「小さなプログラムを組み合わせる」アプローチの中核を担う。

**機能の利用シーン**：システムログの検索（grep）、設定ファイルからの特定フィールド抽出（awk、cut）、ソースコードやファイルの差分比較（diff）、出力の整形・表形式表示（column）など、日常的なシステム運用およびソフトウェア開発のあらゆる場面で利用される。

**主要な処理内容**：
1. grep: 正規表現によるテキストパターン検索。基本正規表現(BRE)、拡張正規表現(ERE)、固定文字列検索に対応。再帰的ディレクトリ検索、圧縮ファイル対応(zgrep)、カラー出力をサポート
2. awk: パターンマッチングとテキスト処理のプログラミング言語。フィールド分割、演算、条件分岐、出力フォーマットを提供するOne True Awk (Brian Kernighan版)
3. diff: 2つのファイルまたはディレクトリの内容を比較し差分を出力。unified/context/normal形式、stone/myers/patience差分アルゴリズム対応
4. cut: テキストの各行から指定フィールドまたは文字位置を切り出す
5. column: テキストを表形式に整形して表示

**関連システム・外部連携**：libz（gzip圧縮ファイル読み取り）、libbz2（bzip2圧縮ファイル読み取り）と連携（grep）。正規表現ライブラリ（libc regex）を使用。

**権限による制御**：特別な権限は不要。一般ユーザで実行可能。ファイルの読み取り権限がない場合はエラーを返す。

## 関連画面

該当なし（CLIコマンドのため画面は存在しない）

## 機能種別

テキスト検索・変換処理 / フィルタ処理

## 入力仕様

### 入力パラメータ

#### grep

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| pattern | string | Yes | 検索パターン（正規表現または固定文字列） | 正規表現として有効であること |
| file | string | No | 検索対象ファイル（省略時は標準入力） | ファイルが存在すること |
| -e pattern | string | No | 複数パターン指定 | 各パターンが有効であること |
| -f file | string | No | パターンファイル指定 | ファイルが存在し読取可能であること |
| -i | flag | No | 大文字小文字無視 | - |
| -v | flag | No | マッチしない行を表示 | - |
| -c | flag | No | マッチ行数のみ出力 | - |
| -l | flag | No | マッチしたファイル名のみ出力 | - |
| -n | flag | No | 行番号を付与 | - |
| -r/-R | flag | No | 再帰的ディレクトリ検索 | - |
| -A num | integer | No | マッチ行の後にnum行表示 | 非負整数 |
| -B num | integer | No | マッチ行の前にnum行表示 | 非負整数 |

#### diff

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| file1 | string | Yes | 比較元ファイル/ディレクトリ | 存在すること |
| file2 | string | Yes | 比較先ファイル/ディレクトリ | 存在すること |
| -u | flag | No | unified形式出力 | - |
| -c | flag | No | context形式出力 | - |
| -r | flag | No | 再帰的ディレクトリ比較 | - |
| -A algorithm | string | No | 差分アルゴリズム(stone/myers/patience) | 有効なアルゴリズム名 |

### 入力データソース

標準入力またはコマンドライン引数で指定されたファイル。パイプライン処理による前段コマンドの出力。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| マッチ行（grep） | string | パターンにマッチした行テキスト |
| 差分出力（diff） | string | unified/context/normal形式の差分テキスト |
| フィールド抽出結果（cut） | string | 指定位置で切り出されたテキスト |
| 整形テキスト（column） | string | 表形式に整形されたテキスト |
| 処理結果（awk） | string | awkプログラムの出力結果 |

### 出力先

標準出力。リダイレクトによりファイル出力可能。

## 処理フロー

### 処理シーケンス（grep）

```
1. コマンドライン引数解析
   └─ getopt_long()によるオプション解析、grepbehave決定（GREP_BASIC/GREP_EXTENDED/GREP_FIXED）
2. パターンコンパイル
   └─ 正規表現パターンをregcomp()でコンパイル、または固定文字列として保持
3. 入力ファイル処理
   └─ 各ファイルに対してgrep_open()でファイルオープン
4. 行単位マッチング
   └─ grep_fgetln()で1行ずつ読み取り、regexec()または文字列比較でマッチ判定
5. 結果出力
   └─ grep_printline()でマッチ行を整形出力（行番号・ファイル名・カラー等）
6. 終了処理
   └─ 終了コード設定（0:マッチあり、1:マッチなし、2:エラー）
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[コマンドライン引数解析]
    B --> C{パターン指定あり?}
    C -->|No| Z[usage表示・終了]
    C -->|Yes| D[パターンコンパイル]
    D --> E{ファイル指定あり?}
    E -->|No| F[標準入力から読み取り]
    E -->|Yes| G[ファイルリスト処理]
    G --> H{再帰モード?}
    H -->|Yes| I[grep_tree: FTS走査]
    H -->|No| J[procfile: 単一ファイル処理]
    I --> J
    F --> J
    J --> K[行単位マッチング]
    K --> L{マッチ?}
    L -->|Yes| M[結果出力]
    L -->|No| N{次の行あり?}
    M --> N
    N -->|Yes| K
    N -->|No| O{次のファイルあり?}
    O -->|Yes| G
    O -->|No| P[終了コード設定]
    P --> Q[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-81-01 | 終了コード規約 | grep: 0=マッチあり、1=マッチなし、2=エラー | 常時 |
| BR-81-02 | 呼び出し名による動作切替 | argv[0]がegrepならGREP_EXTENDED、fgrepならGREP_FIXED | コマンド起動時 |
| BR-81-03 | バイナリファイル検出 | NULバイトを含むファイルをバイナリとして扱い「Binary file matches」を出力 | --binary-files未指定時 |
| BR-81-04 | diff終了コード | 0=差分なし、1=差分あり、2=エラー | 常時 |
| BR-81-05 | diff差分アルゴリズム | stone(デフォルト)、myers、patienceの3種を選択可能 | -Aオプション指定時 |

### 計算ロジック

grepのマッチング処理はPOSIX正規表現（regcomp/regexec）を使用。REG_NOSUBフラグによりマッチ位置の詳細取得を省略し高速化を図る（-oオプション時を除く）。

## データベース操作仕様

該当なし（ファイルシステム上のテキストファイルを直接操作する）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 2 | 正規表現エラー | 無効な正規表現パターン指定 | エラーメッセージを標準エラー出力に表示 |
| 2 | ファイルアクセスエラー | 指定ファイルが存在しないまたは読取不可 | エラーメッセージ出力(-sオプションで抑制可) |
| 2 | メモリ割当エラー | malloc/realloc失敗 | err()で終了 |

### リトライ仕様

リトライは行わない。エラー発生時は即時エラーメッセージ出力（または-sで抑制）し処理継続。

## トランザクション仕様

該当なし（読み取り専用処理。diffの出力は一時ファイルを使用しない）

## パフォーマンス要件

- grepはmmapベースのファイル読み取りをサポートし、大規模ファイルの検索を効率化
- バッファサイズは最大32KB（MAXBUFSIZ）で行単位読み取り
- -mオプションでマッチ数上限を指定し、早期終了による高速化が可能

## セキュリティ考慮事項

- 一般ユーザ権限で動作し、特権は不要
- ファイルのパーミッションに従ったアクセス制御
- 正規表現のバックトラッキングによるReDoS攻撃のリスクは、POSIX regex実装の制約により限定的

## 備考

- grep は BSD grep (GNU compatible) version 2.6.0-FreeBSD として実装
- awkはOne True Awk（Brian Kernighan版）を採用。contrib/one-true-awk/から取り込み
- diffは複数の差分アルゴリズム（stone, myers, patience）をサポートする点がFreeBSD独自の拡張

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する（grep）

grepの内部データ構造を理解することで、パターンマッチングと結果出力の仕組みが把握できる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | grep.h | `usr.bin/grep/grep.h` | パターン構造体(struct pat, struct epat)、ファイル構造体(struct file)、パース文脈(struct parsec)の定義 |

**読解のコツ**: `struct parsec`（103-112行目）がマッチング処理の中心的な状態管理構造体。`regmatch_t matches[MAX_MATCHES]`でマッチ位置を保持する。`grepbehave`変数がGREP_BASIC/GREP_EXTENDED/GREP_FIXEDの3モードを制御する。

#### Step 2: エントリーポイントを理解する（grep）

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | grep.c | `usr.bin/grep/grep.c` | main()関数。オプション解析、パターンコンパイル、ファイル処理の起点 |

**主要処理フロー**:
1. **53-63行目**: エラーメッセージ文字列テーブルの定義
2. **66-67行目**: regcomp/regexecフラグの初期設定（REG_NOSUB | REG_NEWLINE）
3. **86-100行目**: コマンドラインフラグ変数群の宣言。Aflag/Bflagはコンテキスト行数

#### Step 3: ファイル処理層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | file.c | `usr.bin/grep/file.c` | ファイルオープン(grep_open)、行読み取り(grep_fgetln)、mmapサポート |

**主要処理フロー**:
- **51行目**: MAXBUFSIZ=32KB、バッファサイズ上限
- **52行目**: LNBUFBUMP=80、行バッファの増分単位

#### Step 4: マッチングと出力処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | util.c | `usr.bin/grep/util.c` | procfile()によるファイル処理、grep_tree()による再帰検索、結果出力 |
| 4-2 | queue.c | `usr.bin/grep/queue.c` | コンテキスト行（-B/-Aオプション）のキュー管理 |

**主要処理フロー**:
- **59-60行目（util.c）**: struct mprintcによるマッチ出力コンテキスト管理

#### Step 5: diff の構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | diff.h | `usr.bin/diff/diff.h` | 差分アルゴリズム定数（D_DIFFSTONE等）、差分フォーマット定数の定義 |
| 5-2 | diff.c | `usr.bin/diff/diff.c` | main()関数。オプション解析、差分アルゴリズム選択 |
| 5-3 | diffreg.c | `usr.bin/diff/diffreg.c` | 通常ファイル間の差分計算（1736行）。核心の差分アルゴリズム実装 |
| 5-4 | diffdir.c | `usr.bin/diff/diffdir.c` | ディレクトリ間の再帰的差分比較 |

**主要処理フロー**:
- **38行目（diff.c）**: バージョン文字列 "FreeBSD diff 20240307"
- **54-62行目（diff.c）**: アルゴリズムテーブル（stone, myers, patience）

### プログラム呼び出し階層図

```
grep main()
    |
    +-- getopt_long()           # オプション解析
    +-- add_pattern()           # パターン追加
    +-- regcomp()               # 正規表現コンパイル
    |
    +-- grep_tree()             # 再帰ディレクトリ検索
    |       +-- fts_open/read   # ファイルツリー走査
    |       +-- procfile()      # 各ファイル処理
    |
    +-- procfile()              # 単一ファイル処理
            +-- grep_open()     # ファイルオープン
            +-- grep_fgetln()   # 行読み取り
            +-- regexec()       # パターンマッチング
            +-- grep_printline()# 結果出力
            +-- grep_close()    # ファイルクローズ

diff main()
    |
    +-- getopt_long()           # オプション解析
    +-- diffreg()               # 通常ファイル差分
    |       +-- stone/myers/patience アルゴリズム
    |       +-- 差分出力（unified/context/normal）
    +-- diffdir()               # ディレクトリ差分
            +-- 再帰比較
```

### データフロー図

```
[入力]                     [処理]                         [出力]

ファイル/標準入力 ──> grep: regexec()マッチング ──> マッチ行（標準出力）
                     行バッファ(32KB)
                     コンテキストキュー

ファイル1 + ファイル2 ──> diff: 差分アルゴリズム ──> 差分テキスト（標準出力）
                          stone/myers/patience

標準入力/ファイル ──> cut: フィールド/文字切出し ──> 抽出結果（標準出力）

標準入力/ファイル ──> column: 表形式整形 ──> 整形テキスト（標準出力）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| grep.c | `usr.bin/grep/grep.c` | ソース | grepメインプログラム（オプション解析・パターン管理） |
| grep.h | `usr.bin/grep/grep.h` | ヘッダ | grepデータ構造・定数定義 |
| util.c | `usr.bin/grep/util.c` | ソース | ファイル処理・マッチング・出力ユーティリティ |
| file.c | `usr.bin/grep/file.c` | ソース | ファイルI/O（mmap・バッファリング） |
| queue.c | `usr.bin/grep/queue.c` | ソース | コンテキスト行キュー管理 |
| zgrep.sh | `usr.bin/grep/zgrep.sh` | シェルスクリプト | 圧縮ファイル対応grepラッパー |
| diff.c | `usr.bin/diff/diff.c` | ソース | diffメインプログラム |
| diff.h | `usr.bin/diff/diff.h` | ヘッダ | diff定数・構造体定義 |
| diffreg.c | `usr.bin/diff/diffreg.c` | ソース | ファイル間差分計算アルゴリズム |
| diffreg_new.c | `usr.bin/diff/diffreg_new.c` | ソース | 新差分アルゴリズム実装 |
| diffdir.c | `usr.bin/diff/diffdir.c` | ソース | ディレクトリ間差分比較 |
| cut.c | `usr.bin/cut/cut.c` | ソース | cutコマンド実装 |
| column.c | `usr.bin/column/column.c` | ソース | columnコマンド実装 |
| Makefile | `usr.bin/awk/Makefile` | ビルド設定 | awk（One True Awk）ビルド設定 |
