Line data Source code
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 : #if defined (HAVE_SYS_PARAM_H) 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 : 54 : int 55 0 : mailstat(path, st) 56 : const char *path; 57 : struct stat *st; 58 : { 59 0 : static struct stat st_new_last, st_ret_last; 60 0 : struct stat st_ret, st_tmp; 61 0 : DIR *dd; 62 0 : struct dirent *fn; 63 0 : char dir[PATH_MAX * 2], file[PATH_MAX * 2 + 1]; 64 0 : int i, l; 65 0 : time_t atime, mtime; 66 : 67 0 : atime = mtime = 0; 68 : 69 : /* First see if it's a directory. */ 70 0 : if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0) 71 : return i; 72 : 73 0 : if (strlen(path) > sizeof(dir) - 5) 74 : { 75 : #ifdef ENAMETOOLONG 76 0 : errno = ENAMETOOLONG; 77 : #else 78 : errno = EINVAL; 79 : #endif 80 0 : return -1; 81 : } 82 : 83 0 : st_ret = *st; 84 0 : st_ret.st_nlink = 1; 85 0 : st_ret.st_size = 0; 86 : #ifdef HAVE_STRUCT_STAT_ST_BLOCKS 87 0 : st_ret.st_blocks = 0; 88 : #else 89 : st_ret.st_nlink = 0; 90 : #endif 91 0 : st_ret.st_mode &= ~S_IFDIR; 92 0 : st_ret.st_mode |= S_IFREG; 93 : 94 : /* See if cur/ is present */ 95 0 : sprintf(dir, "%s/cur", path); 96 0 : if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 97 : return 0; 98 0 : st_ret.st_atime = st_tmp.st_atime; 99 : 100 : /* See if tmp/ is present */ 101 0 : sprintf(dir, "%s/tmp", path); 102 0 : if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 103 : return 0; 104 0 : st_ret.st_mtime = st_tmp.st_mtime; 105 : 106 : /* And new/ */ 107 0 : sprintf(dir, "%s/new", path); 108 0 : if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0) 109 : return 0; 110 0 : st_ret.st_mtime = st_tmp.st_mtime; 111 : 112 : /* Optimization - if new/ didn't change, nothing else did. */ 113 0 : if (st_tmp.st_dev == st_new_last.st_dev && 114 0 : st_tmp.st_ino == st_new_last.st_ino && 115 0 : st_tmp.st_atime == st_new_last.st_atime && 116 0 : st_tmp.st_mtime == st_new_last.st_mtime) 117 : { 118 0 : *st = st_ret_last; 119 0 : return 0; 120 : } 121 0 : st_new_last = st_tmp; 122 : 123 : /* Loop over new/ and cur/ */ 124 0 : for (i = 0; i < 2; i++) 125 : { 126 0 : sprintf(dir, "%s/%s", path, i ? "cur" : "new"); 127 0 : sprintf(file, "%s/", dir); 128 0 : l = strlen(file); 129 0 : if ((dd = opendir(dir)) == NULL) 130 : return 0; 131 0 : while ((fn = readdir(dd)) != NULL) 132 : { 133 0 : if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file)) 134 : continue; 135 0 : strcpy(file + l, fn->d_name); 136 0 : if (stat(file, &st_tmp) != 0) 137 : continue; 138 0 : st_ret.st_size += st_tmp.st_size; 139 : #ifdef HAVE_STRUCT_STAT_ST_BLOCKS 140 0 : st_ret.st_blocks++; 141 : #else 142 : st_ret.st_nlink++; 143 : #endif 144 0 : if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime) 145 0 : atime = st_tmp.st_atime; 146 0 : if (st_tmp.st_mtime > mtime) 147 0 : mtime = st_tmp.st_mtime; 148 : } 149 0 : closedir(dd); 150 : } 151 : 152 : /* if (atime) */ /* Set atime even if cur/ is empty */ 153 0 : st_ret.st_atime = atime; 154 0 : if (mtime) 155 0 : st_ret.st_mtime = mtime; 156 : 157 0 : *st = st_ret_last = st_ret; 158 0 : return 0; 159 : }