1/* mailstat.c -- stat a mailbox file, handling maildir-type mail directories */ 2 3/* Copyright (C) 2001 Free Software Foundation, Inc. 4 5 This file is part of GNU Bash, the Bourne Again SHell. 6 7 Bash is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 Bash is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Bash. If not, see <http://www.gnu.org/licenses/>. 19*/ 20 21#include <config.h> 22 23#include <stdio.h> 24#include <errno.h> 25 26#include <bashtypes.h> 27#include <posixstat.h> 28#include <posixdir.h> 29#include <bashansi.h> 30 31#ifndef _MINIX 32# include <sys/param.h> 33#endif 34 35#include <maxpath.h> 36 37/* 38 * Stat a file. If it's a maildir, check all messages 39 * in the maildir and present the grand total as a file. 40 * The fields in the 'struct stat' are from the mail directory. 41 * The following fields are emulated: 42 * 43 * st_nlink always 1, unless st_blocks is not present, in which case it's 44 * the total number of messages 45 * st_size total number of bytes in all files 46 * st_blocks total number of messages, if present in struct stat 47 * st_atime access time of newest file in maildir 48 * st_mtime modify time of newest file in maildir 49 * st_mode S_IFDIR changed to S_IFREG 50 * 51 * This is good enough for most mail-checking applications. 52 */ 53 54int 55mailstat(path, st) 56 const char *path; 57 struct stat *st; 58{ 59 static struct stat st_new_last, st_ret_last; 60 struct stat st_ret, st_tmp; 61 DIR *dd; 62 struct dirent *fn; 63 char dir[PATH_MAX * 2], file[PATH_MAX * 2]; 64 int i, l; 65 time_t atime, mtime; 66 67 atime = mtime = 0; 68 69 /* First see if it's a directory. */ 70 if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0) 71 return i; 72 73 if (strlen(path) > sizeof(dir) - 5) 74 { 75#ifdef ENAMETOOLONG 76 errno = ENAMETOOLONG; 77#else 78 errno = EINVAL; 79#endif 80 return -1; 81 } 82 83 st_ret = *st; 84 st_ret.st_nlink = 1; 85 st_ret.st_size = 0; 86#ifdef HAVE_STRUCT_STAT_ST_BLOCKS 87 st_ret.st_blocks = 0; 88#else 89 st_ret.st_nlink = 0; 90#endif 91 st_ret.st_mode &= ~S_IFDIR; 92 st_ret.st_mode |= S_IFREG; 93 94 /* See if cur/ is present */ 95 sprintf(dir, "%s/cur", path); 96 if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 97 return 0; 98 st_ret.st_atime = st_tmp.st_atime; 99 100 /* See if tmp/ is present */ 101 sprintf(dir, "%s/tmp", path); 102 if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 103 return 0; 104 st_ret.st_mtime = st_tmp.st_mtime; 105 106 /* And new/ */ 107 sprintf(dir, "%s/new", path); 108 if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 109 return 0; 110 st_ret.st_mtime = st_tmp.st_mtime; 111 112 /* Optimization - if new/ didn't change, nothing else did. */ 113 if (st_tmp.st_dev == st_new_last.st_dev && 114 st_tmp.st_ino == st_new_last.st_ino && 115 st_tmp.st_atime == st_new_last.st_atime && 116 st_tmp.st_mtime == st_new_last.st_mtime) 117 { 118 *st = st_ret_last; 119 return 0; 120 } 121 st_new_last = st_tmp; 122 123 /* Loop over new/ and cur/ */ 124 for (i = 0; i < 2; i++) 125 { 126 sprintf(dir, "%s/%s", path, i ? "cur" : "new"); 127 sprintf(file, "%s/", dir); 128 l = strlen(file); 129 if ((dd = opendir(dir)) == NULL) 130 return 0; 131 while ((fn = readdir(dd)) != NULL) 132 { 133 if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file)) 134 continue; 135 strcpy(file + l, fn->d_name); 136 if (stat(file, &st_tmp) != 0) 137 continue; 138 st_ret.st_size += st_tmp.st_size; 139#ifdef HAVE_STRUCT_STAT_ST_BLOCKS 140 st_ret.st_blocks++; 141#else 142 st_ret.st_nlink++; 143#endif 144 if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime) 145 atime = st_tmp.st_atime; 146 if (st_tmp.st_mtime > mtime) 147 mtime = st_tmp.st_mtime; 148 } 149 closedir(dd); 150 } 151 152/* if (atime) */ /* Set atime even if cur/ is empty */ 153 st_ret.st_atime = atime; 154 if (mtime) 155 st_ret.st_mtime = mtime; 156 157 *st = st_ret_last = st_ret; 158 return 0; 159} 160