1 /* $Id: miscfuncs.c,v 2.5 2000/06/04 09:09:03 chip Exp $ */
2
3 #include "config.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <ctype.h>
9 #include <fcntl.h>
10 #include <errno.h>
11 #ifdef HAVE_UNISTD_H
12 # include <unistd.h>
13 #endif
14 #ifdef HAVE_STDLIB_H
15 # include <stdlib.h>
16 #endif
17 #include <time.h>
18 #ifdef TM_IN_SYS_TIME
19 # include <sys/time.h>
20 #endif
21 #ifdef HAVE_TERMIOS_H
22 # include <termios.h>
23 #else
24 # include <termio.h>
25 # define termios termio
26 #endif
27
28 #ifdef HAVE_DIRENT_H
29 # include <dirent.h>
30 # define NAMLEN(dirent) strlen((dirent)->d_name)
31 #else
32 # define dirent direct
33 # define NAMLEN(dirent) (dirent)->d_namlen
34 # if HAVE_SYS_NDIR_H
35 # include <sys/ndir.h>
36 # endif
37 # if HAVE_SYS_DIR_H
38 # include <sys/dir.h>
39 # endif
40 # if HAVE_NDIR_H
41 # include <ndir.h>
42 # endif
43 #endif
44
45 #ifndef STDC_HEADERS
46 extern VOID *malloc(), *realloc();
47 extern char *strcpy(), *strrchr();
48 #endif
49
50 #ifndef STDIN_FILENO
51 # define STDIN_FILENO 0
52 #endif
53
54 #include "xtail.h"
55
56
57 /*
58 * Scan a directory for files not currently on a list.
59 */
scan_directory(const char * dirname)60 int scan_directory(const char *dirname)
61 {
62 register int i;
63 register struct dirent *dp;
64 register struct entry_descrip **elist, *entryp;
65 char *basename;
66 struct stat sbuf;
67 DIR *dirp;
68 static char pathname[MAXNAMLEN];
69
70 Dprintf(stderr, ">>> scanning directory '%s'\n", dirname);
71 if ((dirp = opendir(dirname)) == NULL)
72 return -1;
73
74 (void) strcat(strcpy(pathname, dirname), "/");
75 basename = pathname + strlen(pathname);
76
77 #define SKIP_DIR(D) \
78 (D[0] == '.' && (D[1] == '\0' || (D[1] == '.' && D[2] == '\0')))
79
80 while ((dp = readdir(dirp)) != NULL) {
81
82 if (SKIP_DIR(dp->d_name))
83 continue;
84 (void) strcpy(basename, dp->d_name);
85 if (stat(pathname, &sbuf) != 0)
86 continue;
87 if ((sbuf.st_mode & S_IFMT) != S_IFREG)
88 continue;
89
90 for (i = List_file->num_entries, elist=List_file->list ; i > 0 ; --i, ++elist) {
91 if (strcmp((*elist)->name, pathname) == 0)
92 break;
93 }
94 if (i > 0)
95 continue;
96
97 for (i = List_zap->num_entries, elist=List_zap->list ; i > 0 ; --i, ++elist) {
98 if (strcmp((*elist)->name, pathname) == 0)
99 break;
100 }
101 if (i > 0)
102 continue;
103
104 entryp = new_entry(List_file, pathname);
105 if (Reset_status) {
106 message(MSSG_CREATED, entryp);
107 } else {
108 entryp->mtime = sbuf.st_mtime;
109 entryp->size = sbuf.st_size;
110 }
111
112 }
113
114 (void) closedir(dirp);
115 return 0;
116
117 }
118
119
120 /*
121 * Compare mtime of two entries. Used by the "qsort()" in "fixup_open_files()".
122 */
ecmp(const void * p1,const void * p2)123 int ecmp(const void *p1, const void *p2)
124 {
125 const struct entry_descrip **ep1 = (const struct entry_descrip **) p1;
126 const struct entry_descrip **ep2 = (const struct entry_descrip **) p2;
127 return ((*ep2)->mtime - (*ep1)->mtime);
128 }
129
130
131 /*
132 * Manage the open files.
133 * A small number of entries in "List_file" are kept open to minimize
134 * the overhead in checking for changes. The strategy is to make sure
135 * the MAX_OPEN most recently modified files are all open.
136 */
fixup_open_files(void)137 void fixup_open_files(void)
138 {
139 register int i;
140 register struct entry_descrip **elist;
141
142 Dprintf(stderr, ">>> resorting file list\n");
143 (void) qsort(
144 (char *) List_file->list,
145 List_file->num_entries,
146 sizeof(struct entry_descrip *),
147 ecmp);
148 Sorted = TRUE;
149
150 /*
151 * Start at the end of the list.
152 */
153 i = last_entry(List_file);
154 elist = &List_file->list[i];
155
156 /*
157 * All the files at the end of the list should be closed.
158 */
159 for ( ; i >= MAX_OPEN ; --i, --elist) {
160 if ((*elist)->fd > 0) {
161 (void) close((*elist)->fd);
162 (*elist)->fd = 0;
163 }
164 }
165
166 /*
167 * The first MAX_OPEN files in the list should be open.
168 */
169 for ( ; i >= 0 ; --i, --elist) {
170 if ((*elist)->fd <= 0)
171 (void) open_entry(List_file, i);
172 }
173
174 }
175
176
177 /*
178 * Standard message interface.
179 * There are two reasons for this message interface. First, it provides
180 * consistent diagnostics for all the messages. Second, it manages the
181 * filename banner display whenever we switch to a different file.
182 * Warning - "errno" is used in some of the messages, so care must be
183 * taken not to step on it before message() can be called.
184 */
message(int sel,const struct entry_descrip * e)185 void message(int sel, const struct entry_descrip *e)
186 {
187 static char *ofile = NULL;
188
189 /*
190 * Don't display the file banner if the file hasn't changed since last time.
191 */
192 if (sel == MSSG_BANNER && ofile != NULL && strcmp(ofile, e->name) == 0)
193 return;
194
195 /*
196 * Make sure the message selector is within range.
197 */
198 if (sel < 0 || sel > MSSG_UNKNOWN)
199 sel = MSSG_UNKNOWN;
200
201 /*
202 * Display the message.
203 */
204 if (mssg_list[sel] != NULL)
205 (void) printf(mssg_list[sel], e->name, strerror(errno));
206
207 ofile = (sel == MSSG_BANNER ? e->name : NULL);
208 }
209
210
211 /*
212 * Display currently opened files.
213 */
show_status(void)214 void show_status(void)
215 {
216 int i, n;
217 struct tm *tp;
218 static char *monname[] = {
219 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
220 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
221 };
222 static time_t tval0;
223 time_t tval;
224
225 /* give user a hint if they are whacking on SIGINT */
226 time(&tval);
227 if (tval0 != 0 && difftime(tval, tval0) < 3)
228 (void) printf("(note: use \"%s\" SIQUIT to exit program)\n", quit_ch());
229 tval0 = tval;
230
231 (void) printf("\n*** recently changed files ***\n");
232 for (i = 0, n = 0 ; i < List_file->num_entries ; ++i) {
233 if (List_file->list[i]->fd > 0) {
234 tp = localtime(&List_file->list[i]->mtime);
235 (void) printf("%4d %2d-%3s-%02d %02d:%02d:%02d %s\n",
236 ++n,
237 tp->tm_mday, monname[tp->tm_mon], (tp->tm_year % 100),
238 tp->tm_hour, tp->tm_min, tp->tm_sec,
239 List_file->list[i]->name
240 );
241 }
242 }
243
244 (void) printf(
245 "currently watching: %d files %d dirs %d unknown entries\n",
246 List_file->num_entries, List_dir->num_entries, List_zap->num_entries);
247
248 message(MSSG_NONE, (struct entry_descrip *) NULL);
249
250 }
251
252
quit_ch(void)253 char *quit_ch(void)
254 {
255 struct termios tty;
256 static char buf[8];
257 int c;
258
259 if (buf[0] != '\0')
260 return buf;
261
262 if (tcgetattr(STDIN_FILENO, &tty) < 0) {
263 strcpy(buf, "?err?");
264 return buf;
265 }
266
267 c = tty.c_cc[VQUIT];
268 if (isprint(c) && !isspace(c)) {
269 buf[0] = c;
270 buf[1] = '\0';
271 } else if (iscntrl(c)) {
272 sprintf(buf, "ctrl-%c", c+0x40);
273 } else {
274 sprintf(buf, "0x%02X", c);
275 }
276 return buf;
277 }
278
279
safe_malloc(size_t n)280 VOID *safe_malloc(size_t n)
281 {
282 VOID *p;
283 if ((p = malloc(n)) == NULL) {
284 fprintf(stderr, "%s: malloc(%d) failed\n", Progname, n);
285 exit(2);
286 }
287 return p;
288 }
289
safe_realloc(VOID * p,size_t n)290 VOID *safe_realloc(VOID *p, size_t n)
291 {
292 VOID *p1;
293 if ((p1 = realloc(p, n)) == NULL) {
294 fprintf(stderr, "%s: realloc(%d) failed\n", Progname, n);
295 exit(2);
296 }
297 return p1;
298 }
299
300
safe_strdup(const char * p)301 char *safe_strdup(const char *p)
302 {
303 return strcpy(safe_malloc(strlen(p)+1), p);
304 }
305
306
basename(char * p)307 char *basename(char *p)
308 {
309 char *q = strrchr(p, '/');
310 return (q ? q+1 : p);
311 }
312
313
314 #ifndef HAVE_DIFFTIME
difftime(time_t t1,time_t t0)315 double difftime(time_t t1, time_t t0)
316 {
317 double d1, d0;
318 d1 = t1;
319 d0 = t0;
320 return (d1 - d0);
321 }
322 #endif
323
324
325 #ifndef HAVE_STRERROR
326 extern int errno;
327 extern char *sys_errlist[];
strerror(int err)328 char *strerror(int err)
329 {
330 return sys_errlist[err];
331 }
332 #endif
333
334