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