        -:    0:Source:/__w/c-modernization-kit/c-modernization-kit/test/util/src/libutilTest/trace_providerTest/file-provider.c
        -:    0:Graph:generated by cobertura2gcov
        -:    0:Data:cobertura.xml
        -:    1:#include <trace-file-util.h>
        -:    2:#include <stdlib.h>
        -:    3:#include <string.h>
        -:    4:#include <stdio.h>
        -:    5:
        -:    6:#ifdef _WIN32
        -:    7:#include <windows.h>
        -:    8:#else /* !_WIN32 */
        -:    9:#include <fcntl.h>
        -:   10:#include <unistd.h>
        -:   11:#include <sys/stat.h>
        -:   12:#include <sys/types.h>
        -:   13:#include <pthread.h>
        -:   14:#include <time.h>
        -:   15:#include <limits.h>
        -:   16:#endif /* _WIN32 */
        -:   17:
        -:   18:/* ===== 内部定数 ===== */
        -:   19:
        -:   20:/** 1 行分のスタックバッファサイズ。 */
        -:   21:#define TRACE_FILE_LINE_BUF   1100
        -:   22:
        -:   23:/** ファイル書き込みロック取得のタイムアウト (ミリ秒)。 */
        -:   24:#define FILE_LOCK_TIMEOUT_MS  100
        -:   25:
        -:   26:/** タイムスタンプ部分の文字数 ("YYYY-MM-DD HH:MM:SS.mmm" = 23 文字)。 */
        -:   27:#define TRACE_FILE_TS_LEN     23
        -:   28:
        -:   29:/** ローテーションパスのサフィックス最大長 (".999\0" = 5 文字)。 */
        -:   30:#define TRACE_FILE_SUFFIX_MAX 5
        -:   31:
        -:   32:/* プラットフォームごとのパス長上限 */
        -:   33:#ifdef _WIN32
        -:   34:#define TRACE_FILE_PATH_MAX   MAX_PATH
        -:   35:#else /* !_WIN32 */
        -:   36:#ifndef PATH_MAX
        -:   37:#define PATH_MAX 4096
        -:   38:#endif /* PATH_MAX */
        -:   39:#define TRACE_FILE_PATH_MAX   PATH_MAX
        -:   40:#endif /* _WIN32 */
        -:   41:
        -:   42:/* ===== 内部構造体 ===== */
        -:   43:
        -:   44:/**
        -:   45: *  @brief  ファイルトレースプロバイダハンドル構造体 (内部定義)。
        -:   46: */
        -:   47:struct trace_file_provider
        -:   48:{
        -:   49:    /** ヒープ確保済みファイルパス文字列。 */
        -:   50:    char   *path;
        -:   51:    /** ファイル 1 世代あたりの最大バイト数。 */
        -:   52:    size_t  max_bytes;
        -:   53:    /** 現ファイルへの書き込み済みバイト数 (インメモリ追跡)。 */
        -:   54:    size_t  current_bytes;
        -:   55:    /** 保持する旧世代数。 */
        -:   56:    int     generations;
        -:   57:
        -:   58:#ifdef _WIN32
        -:   59:    /** ファイルハンドル。INVALID_HANDLE_VALUE = 未開。 */
        -:   60:    HANDLE           fh;
        -:   61:    /** スレッド安全のためのクリティカルセクション。 */
        -:   62:    CRITICAL_SECTION cs;
        -:   63:    /** cs が初期化済みかどうかのフラグ。 */
        -:   64:    int              cs_initialized;
        -:   65:#else /* !_WIN32 */
        -:   66:    /** ファイルディスクリプタ。-1 = 未開。 */
        -:   67:    int              fd;
        -:   68:    /** スレッド安全のための mutex。 */
        -:   69:    pthread_mutex_t  mutex;
        -:   70:    /** mutex が初期化済みかどうかのフラグ。 */
        -:   71:    int              mutex_initialized;
        -:   72:    /** パディング (構造体サイズを 8 バイト境界に揃える)。 */
        -:   73:    int              _pad_end;
        -:   74:#endif /* _WIN32 */
        -:   75:};
        -:   76:
        -:   77:/* ===== 内部ヘルパー関数 ===== */
        -:   78:
        -:   79:/**
        -:   80: *  @brief  トレースレベル整数をレベル文字に変換する。
        -:   81: */
       16:   82:static char level_char(int level)
        -:   83:{
       16:   84:    switch (level)
        -:   85:    {
    #####:   86:    case TRACE_FILE_LV_CRITICAL: return 'C';
        8:   87:    case TRACE_FILE_LV_ERROR:    return 'E';
        2:   88:    case TRACE_FILE_LV_WARNING:  return 'W';
        4:   89:    case TRACE_FILE_LV_INFO:     return 'I';
        2:   90:    default:                     return 'V';
        -:   91:    }
        -:   92:}
        -:   93:
        -:   94:/**
        -:   95: *  @brief  現在時刻を "YYYY-MM-DD HH:MM:SS.mmm" (UTC) 形式でバッファへ書き込む。
        -:   96: *  @param  buf      書き込み先バッファ。
        -:   97: *  @param  buf_size バッファサイズ (TRACE_FILE_TS_LEN + 1 以上を推奨)。
        -:   98: */
       16:   99:static void format_timestamp(char *buf, int buf_size)
        -:  100:{
        -:  101:#ifdef _WIN32
        -:  102:    SYSTEMTIME st;
        -:  103:    GetSystemTime(&st);
        -:  104:    snprintf(buf, (size_t)buf_size,
        -:  105:             "%04d-%02d-%02d %02d:%02d:%02d.%03d",
        -:  106:             (int)st.wYear,         (int)st.wMonth,  (int)st.wDay,
        -:  107:             (int)st.wHour,         (int)st.wMinute, (int)st.wSecond,
        -:  108:             (int)st.wMilliseconds);
        -:  109:#else /* !_WIN32 */
        -:  110:    struct timespec ts;
        -:  111:    struct tm       tm_val;
       16:  112:    clock_gettime(CLOCK_REALTIME, &ts);
       16:  113:    gmtime_r(&ts.tv_sec, &tm_val);
        -:  114:    /* -Wformat-truncation の抑制: gmtime_r() が返す tm 構造体の各フィールドは POSIX で
        -:  115:     * 範囲が保証されており (tm_mon: 0-11, tm_mday: 1-31 等)、出力は常に 23 文字以内に
        -:  116:     * 収まる。GCC は int 型の理論上の最大範囲 [-2147483648, 2147483647] を使って静的
        -:  117:     * 検証するため false positive が発生する。pragma はその誤報を局所的に抑制する。 */
        -:  118:#pragma GCC diagnostic push
        -:  119:#pragma GCC diagnostic ignored "-Wformat-truncation"
       48:  120:    snprintf(buf, (size_t)buf_size,
        -:  121:             "%04d-%02d-%02d %02d:%02d:%02d.%03d",
       32:  122:             tm_val.tm_year + 1900, tm_val.tm_mon + 1, tm_val.tm_mday,
        -:  123:             tm_val.tm_hour,        tm_val.tm_min,      tm_val.tm_sec,
       16:  124:             (int)(ts.tv_nsec / 1000000));
        -:  125:#pragma GCC diagnostic pop
        -:  126:#endif /* _WIN32 */
       16:  127:}
        -:  128:
        -:  129:/**
        -:  130: *  @brief  ファイルを追記モードで開き current_bytes を初期サイズで初期化する。
        -:  131: *  @return 成功 0 / 失敗 -1。
        -:  132: */
        8:  133:static int open_file(trace_file_provider_t *p)
        -:  134:{
        -:  135:#ifdef _WIN32
        -:  136:    LARGE_INTEGER pos;
        -:  137:    LARGE_INTEGER size;
        -:  138:
        -:  139:    p->fh = CreateFileA(
        -:  140:        p->path,
        -:  141:        GENERIC_WRITE,
        -:  142:        FILE_SHARE_READ | FILE_SHARE_DELETE,
        -:  143:        NULL,
        -:  144:        OPEN_ALWAYS,
        -:  145:        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
        -:  146:        NULL);
        -:  147:
        -:  148:    if (p->fh == INVALID_HANDLE_VALUE)
        -:  149:    {
        -:  150:        p->current_bytes = 0;
        -:  151:        return -1;
        -:  152:    }
        -:  153:
        -:  154:    /* 末尾へ移動して追記モードにする */
        -:  155:    pos.QuadPart = 0;
        -:  156:    SetFilePointerEx(p->fh, pos, NULL, FILE_END);
        -:  157:
        -:  158:    /* 既存ファイルサイズを取得してインメモリカウンタを初期化する */
        -:  159:    if (GetFileSizeEx(p->fh, &size))
        -:  160:    {
        -:  161:        p->current_bytes = (size_t)size.QuadPart;
        -:  162:    }
        -:  163:    else
        -:  164:    {
        -:  165:        p->current_bytes = 0;
        -:  166:    }
        -:  167:
        -:  168:    return 0;
        -:  169:
        -:  170:#else /* !_WIN32 */
        -:  171:    struct stat st;
        -:  172:
        8:  173:    p->fd = open(p->path,
        -:  174:                 O_WRONLY | O_APPEND | O_CREAT | O_DSYNC,
        -:  175:                 0644);
        -:  176:
        8:  177:    if (p->fd == -1)
        -:  178:    {
    #####:  179:        p->current_bytes = 0;
    #####:  180:        return -1;
        -:  181:    }
        -:  182:
        -:  183:    /* 既存ファイルサイズを取得してインメモリカウンタを初期化する */
        8:  184:    if (fstat(p->fd, &st) == 0)
        -:  185:    {
        8:  186:        p->current_bytes = (size_t)st.st_size;
        -:  187:    }
        -:  188:    else
        -:  189:    {
    #####:  190:        p->current_bytes = 0;
        -:  191:    }
        -:  192:
        8:  193:    return 0;
        -:  194:#endif /* _WIN32 */
        -:  195:}
        -:  196:
        -:  197:/**
        -:  198: *  @brief  ローテーション後の新規ファイルを空で作成して開く。
        -:  199: *          current_bytes は必ず 0 に設定される。
        -:  200: *  @return 成功 0 / 失敗 -1。
        -:  201: */
    #####:  202:static int open_file_truncate(trace_file_provider_t *p)
        -:  203:{
    #####:  204:    p->current_bytes = 0;
        -:  205:
        -:  206:#ifdef _WIN32
        -:  207:    p->fh = CreateFileA(
        -:  208:        p->path,
        -:  209:        GENERIC_WRITE,
        -:  210:        FILE_SHARE_READ | FILE_SHARE_DELETE,
        -:  211:        NULL,
        -:  212:        CREATE_ALWAYS,
        -:  213:        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
        -:  214:        NULL);
        -:  215:
        -:  216:    return (p->fh != INVALID_HANDLE_VALUE) ? 0 : -1;
        -:  217:
        -:  218:#else /* !_WIN32 */
    #####:  219:    p->fd = open(p->path,
        -:  220:                 O_WRONLY | O_APPEND | O_CREAT | O_TRUNC | O_DSYNC,
        -:  221:                 0644);
        -:  222:
    #####:  223:    return (p->fd != -1) ? 0 : -1;
        -:  224:#endif /* _WIN32 */
        -:  225:}
        -:  226:
        -:  227:/**
        -:  228: *  @brief  開いているファイルを閉じる。未開の場合は何もしない (冪等)。
        -:  229: */
        8:  230:static void close_file(trace_file_provider_t *p)
        -:  231:{
        -:  232:#ifdef _WIN32
        -:  233:    if (p->fh != INVALID_HANDLE_VALUE)
        -:  234:    {
        -:  235:        CloseHandle(p->fh);
        -:  236:        p->fh = INVALID_HANDLE_VALUE;
        -:  237:    }
        -:  238:#else /* !_WIN32 */
        8:  239:    if (p->fd != -1)
        -:  240:    {
        8:  241:        close(p->fd);
        8:  242:        p->fd = -1;
        -:  243:    }
        -:  244:#endif /* _WIN32 */
        8:  245:}
        -:  246:
        -:  247:/**
        -:  248: *  @brief  トレースファイルをローテーションする。
        -:  249: *  @details ロック保持中から呼ばれる。\n
        -:  250: *           リネームに失敗した場合はその世代でカスケードを打ち切り、
        -:  251: *           呼び出し元をブロックせずに続行する (ベストエフォート)。
        -:  252: */
    #####:  253:static void rotate_file(trace_file_provider_t *p)
        -:  254:{
        -:  255:    /* パス構築用スタックバッファ */
        -:  256:    char old_path[TRACE_FILE_PATH_MAX];
        -:  257:    char new_path[TRACE_FILE_PATH_MAX];
        -:  258:    int  gen;
        -:  259:
    #####:  260:    close_file(p);
        -:  261:
        -:  262:    /* 最老世代のファイルを削除する (失敗は無視) */
    #####:  263:    snprintf(new_path, sizeof(new_path), "%s.%d", p->path, p->generations);
        -:  264:#ifdef _WIN32
        -:  265:    DeleteFileA(new_path);
        -:  266:#else /* !_WIN32 */
    #####:  267:    unlink(new_path);
        -:  268:#endif /* _WIN32 */
        -:  269:
        -:  270:    /* path.(gen-1) → path.gen のカスケードリネーム */
    #####:  271:    for (gen = p->generations; gen >= 1; gen--)
        -:  272:    {
        -:  273:        /* 移動先: path.gen */
    #####:  274:        snprintf(new_path, sizeof(new_path), "%s.%d", p->path, gen);
        -:  275:
        -:  276:        /* 移動元: gen==1 のときは path そのもの */
    #####:  277:        if (gen == 1)
        -:  278:        {
        -:  279:            /* old_path に path をコピー */
    #####:  280:            snprintf(old_path, sizeof(old_path), "%s", p->path);
        -:  281:        }
        -:  282:        else
        -:  283:        {
    #####:  284:            snprintf(old_path, sizeof(old_path), "%s.%d", p->path, gen - 1);
        -:  285:        }
        -:  286:
        -:  287:#ifdef _WIN32
        -:  288:        if (!MoveFileExA(old_path, new_path, MOVEFILE_REPLACE_EXISTING))
        -:  289:        {
        -:  290:            /* リネーム失敗: カスケードをここで打ち切る */
        -:  291:            break;
        -:  292:        }
        -:  293:#else /* !_WIN32 */
    #####:  294:        if (rename(old_path, new_path) != 0)
        -:  295:        {
        -:  296:            /* リネーム失敗: カスケードをここで打ち切る */
    #####:  297:            break;
        -:  298:        }
        -:  299:#endif /* _WIN32 */
        -:  300:    }
        -:  301:
        -:  302:    /* 新規ファイルを作成して開く (失敗しても fh=INVALID/fd=-1 のまま続行) */
    #####:  303:    open_file_truncate(p);
    #####:  304:}
        -:  305:
        -:  306:/* ===== 公開 API ===== */
        -:  307:
        -:  308:trace_file_provider_t *TRACE_FILE_UTIL_API
        8:  309:    trace_file_provider_init(const char *path, size_t max_bytes, int generations)
        -:  310:{
        -:  311:    trace_file_provider_t *handle;
        -:  312:    size_t path_len;
        -:  313:
        8:  314:    if (path == NULL)
        -:  315:    {
    #####:  316:        return NULL;
        -:  317:    }
        -:  318:
        8:  319:    path_len = strlen(path);
        -:  320:
        -:  321:    /* パスが長すぎてローテーションサフィックスを付加できない場合は拒否する */
        8:  322:    if (path_len + TRACE_FILE_SUFFIX_MAX >= (size_t)TRACE_FILE_PATH_MAX)
        -:  323:    {
    #####:  324:        return NULL;
        -:  325:    }
        -:  326:
        8:  327:    handle = (trace_file_provider_t *)malloc(sizeof(trace_file_provider_t));
        8:  328:    if (handle == NULL)
        -:  329:    {
    #####:  330:        return NULL;
        -:  331:    }
        -:  332:
        -:  333:    /* パス文字列をヒープへ複製する */
        8:  334:    handle->path = (char *)malloc(path_len + 1);
        8:  335:    if (handle->path == NULL)
        -:  336:    {
    #####:  337:        free(handle);
    #####:  338:        return NULL;
        -:  339:    }
        8:  340:    memcpy(handle->path, path, path_len + 1);
        -:  341:
        8:  342:    handle->max_bytes    = (max_bytes > 0)    ? max_bytes    : TRACE_FILE_DEFAULT_MAX_BYTES;
        8:  343:    handle->generations  = (generations > 0)  ? generations  : TRACE_FILE_DEFAULT_GENERATIONS;
        8:  344:    handle->current_bytes = 0;
        -:  345:
        -:  346:    /* プラットフォームごとの同期プリミティブを初期化する */
        -:  347:#ifdef _WIN32
        -:  348:    handle->fh             = INVALID_HANDLE_VALUE;
        -:  349:    handle->cs_initialized = 0;
        -:  350:    InitializeCriticalSectionAndSpinCount(&handle->cs, 1000);
        -:  351:    handle->cs_initialized = 1;
        -:  352:#else /* !_WIN32 */
        8:  353:    handle->fd                  = -1;
        8:  354:    handle->mutex_initialized   = 0;
        8:  355:    if (pthread_mutex_init(&handle->mutex, NULL) != 0)
        -:  356:    {
    #####:  357:        free(handle->path);
    #####:  358:        free(handle);
    #####:  359:        return NULL;
        -:  360:    }
        8:  361:    handle->mutex_initialized = 1;
        -:  362:#endif /* _WIN32 */
        -:  363:
        -:  364:    /* ファイルを開く; 失敗したらリソースを解放して NULL を返す */
        8:  365:    if (open_file(handle) != 0)
        -:  366:    {
        -:  367:#ifdef _WIN32
        -:  368:        if (handle->cs_initialized)
        -:  369:        {
        -:  370:            DeleteCriticalSection(&handle->cs);
        -:  371:        }
        -:  372:#else /* !_WIN32 */
    #####:  373:        if (handle->mutex_initialized)
        -:  374:        {
    #####:  375:            pthread_mutex_destroy(&handle->mutex);
        -:  376:        }
        -:  377:#endif /* _WIN32 */
    #####:  378:        free(handle->path);
    #####:  379:        free(handle);
    #####:  380:        return NULL;
        -:  381:    }
        -:  382:
        8:  383:    return handle;
        -:  384:}
        -:  385:
        -:  386:int TRACE_FILE_UTIL_API
       16:  387:    trace_file_provider_write(trace_file_provider_t *handle, int level,
        -:  388:                              const char *message)
        -:  389:{
        -:  390:    char ts[TRACE_FILE_TS_LEN + 1];
        -:  391:    char buf[TRACE_FILE_LINE_BUF];
        -:  392:    int  len;
        -:  393:    int  ret;
        -:  394:
       16:  395:    if (handle == NULL || message == NULL)
        -:  396:    {
    #####:  397:        return 0;
        -:  398:    }
        -:  399:
        -:  400:    /* タイムスタンプはロック外で取得する (共有状態へのアクセスなし) */
       16:  401:    format_timestamp(ts, (int)sizeof(ts));
        -:  402:
        -:  403:    /* 1 行全体をスタックバッファへフォーマットする (syscall 回数を最小化) */
       16:  404:    len = snprintf(buf, sizeof(buf), "%s %c %s\n", ts, level_char(level), message);
       16:  405:    if (len <= 0)
        -:  406:    {
    #####:  407:        return -1;
        -:  408:    }
       16:  409:    if (len >= (int)sizeof(buf))
        -:  410:    {
        -:  411:        /* 切り詰め: バッファ末尾を必ず改行で終端する */
    #####:  412:        len = (int)sizeof(buf) - 1;
    #####:  413:        buf[len - 1] = '\n';
        -:  414:    }
        -:  415:
        -:  416:    /* ロック取得 (タイムアウト付き) */
        -:  417:#ifdef _WIN32
        -:  418:    {
        -:  419:        DWORD deadline = GetTickCount() + (DWORD)FILE_LOCK_TIMEOUT_MS;
        -:  420:        while (!TryEnterCriticalSection(&handle->cs))
        -:  421:        {
        -:  422:            if ((LONG)(GetTickCount() - deadline) >= 0)
        -:  423:            {
        -:  424:                return -1;
        -:  425:            }
        -:  426:            SwitchToThread();
        -:  427:        }
        -:  428:    }
        -:  429:#else /* !_WIN32 */
        -:  430:    {
        -:  431:        struct timespec abs_timeout;
       16:  432:        clock_gettime(CLOCK_REALTIME, &abs_timeout);
       16:  433:        abs_timeout.tv_nsec += (long)FILE_LOCK_TIMEOUT_MS * 1000000L;
       16:  434:        if (abs_timeout.tv_nsec >= 1000000000L)
        -:  435:        {
    #####:  436:            abs_timeout.tv_sec  += 1;
    #####:  437:            abs_timeout.tv_nsec -= 1000000000L;
        -:  438:        }
       16:  439:        if (pthread_mutex_timedlock(&handle->mutex, &abs_timeout) != 0)
        -:  440:        {
    #####:  441:            return -1;
        -:  442:        }
        -:  443:    }
        -:  444:#endif /* _WIN32 */
        -:  445:
        -:  446:    /* ファイルが未開の場合は書き込みをスキップする */
        -:  447:#ifdef _WIN32
        -:  448:    if (handle->fh == INVALID_HANDLE_VALUE)
        -:  449:    {
        -:  450:        LeaveCriticalSection(&handle->cs);
        -:  451:        return -1;
        -:  452:    }
        -:  453:#else /* !_WIN32 */
       16:  454:    if (handle->fd == -1)
        -:  455:    {
    #####:  456:        pthread_mutex_unlock(&handle->mutex);
    #####:  457:        return -1;
        -:  458:    }
        -:  459:#endif /* _WIN32 */
        -:  460:
        -:  461:    /* ファイルへ書き込む (FILE_FLAG_WRITE_THROUGH / O_DSYNC により自動フラッシュ) */
       16:  462:    ret = -1;
        -:  463:#ifdef _WIN32
        -:  464:    {
        -:  465:        DWORD written = 0;
        -:  466:        if (WriteFile(handle->fh, buf, (DWORD)len, &written, NULL)
        -:  467:            && (DWORD)written == (DWORD)len)
        -:  468:        {
        -:  469:            ret = 0;
        -:  470:        }
        -:  471:    }
        -:  472:#else /* !_WIN32 */
        -:  473:    {
       16:  474:        ssize_t written = write(handle->fd, buf, (size_t)len);
       16:  475:        if (written == (ssize_t)len)
        -:  476:        {
       16:  477:            ret = 0;
        -:  478:        }
        -:  479:    }
        -:  480:#endif /* _WIN32 */
        -:  481:
        -:  482:    /* 書き込み成功時: サイズを追跡しローテーション閾値を確認する */
       16:  483:    if (ret == 0)
        -:  484:    {
        -:  485:        /* size_t のオーバーフローを防ぐ (実用上は発生しないが防御的に扱う) */
       16:  486:        if (handle->current_bytes <= (size_t)-1 - (size_t)len)
        -:  487:        {
       16:  488:            handle->current_bytes += (size_t)len;
        -:  489:        }
        -:  490:
       16:  491:        if (handle->current_bytes >= handle->max_bytes)
        -:  492:        {
    #####:  493:            rotate_file(handle);
        -:  494:        }
        -:  495:    }
        -:  496:
        -:  497:    /* ロック解放 */
        -:  498:#ifdef _WIN32
        -:  499:    LeaveCriticalSection(&handle->cs);
        -:  500:#else /* !_WIN32 */
       16:  501:    pthread_mutex_unlock(&handle->mutex);
        -:  502:#endif /* _WIN32 */
        -:  503:
       16:  504:    return ret;
        -:  505:}
        -:  506:
        -:  507:void TRACE_FILE_UTIL_API
        8:  508:    trace_file_provider_dispose(trace_file_provider_t *handle)
        -:  509:{
        8:  510:    if (handle == NULL)
        -:  511:    {
    #####:  512:        return;
        -:  513:    }
        -:  514:
        8:  515:    close_file(handle);
        -:  516:
        -:  517:#ifdef _WIN32
        -:  518:    if (handle->cs_initialized)
        -:  519:    {
        -:  520:        DeleteCriticalSection(&handle->cs);
        -:  521:        handle->cs_initialized = 0;
        -:  522:    }
        -:  523:#else /* !_WIN32 */
        8:  524:    if (handle->mutex_initialized)
        -:  525:    {
        8:  526:        pthread_mutex_destroy(&handle->mutex);
        8:  527:        handle->mutex_initialized = 0;
        -:  528:    }
        -:  529:#endif /* _WIN32 */
        -:  530:
        8:  531:    free(handle->path);
        8:  532:    free(handle);
        -:  533:}
