home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume07 / xtail < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  36.7 KB

  1. From decwrl!ucbvax!tut.cis.ohio-state.edu!brutus.cs.uiuc.edu!wuarchive!wugate!uunet!allbery Thu Aug  3 08:51:46 PDT 1989
  2. Article 1000 of comp.sources.misc:
  3. Path: decwrl!ucbvax!tut.cis.ohio-state.edu!brutus.cs.uiuc.edu!wuarchive!wugate!uunet!allbery
  4. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  5. Newsgroups: comp.sources.misc
  6. Subject: v07i108: xtail - a kind of "tail -f" for multiple files
  7. Message-ID: <61723@uunet.UU.NET>
  8. Date: 28 Jul 89 01:24:35 GMT
  9. Sender: allbery@uunet.UU.NET
  10. Reply-To: chip@vector.Dallas.TX.US.UUCP (Chip Rosenthal)
  11. Organization: Dallas Semiconductor
  12. Lines: 1328
  13. Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  14.  
  15. Posting-number: Volume 7, Issue 108
  16. Submitted-by: chip@vector.Dallas.TX.US.UUCP (Chip Rosenthal)
  17. Archive-name: xtail
  18.  
  19. "xtail" watches the growth of files.  It is similar to "tail -f", but may
  20. watch many files at once.  The syntax is:
  21.  
  22.     xtail pathname ...
  23.     
  24. "xtail" will monitor all the specified files and display information added
  25. to them.  If you specify a directory name, "xtail" will watch all the
  26. files in that directory - including those created after "xtail" was
  27. started.  If you give "xtail" a name which doesn't exist, it will watch
  28. for the creation of the named entry.  My favorite usage is:
  29.  
  30.     xtail /usr/spool/uucp/.Log/*
  31.  
  32. --- cut here -----------------------------------------------------------------
  33. #! /bin/sh
  34. # this is a "shar" archive - run through "/bin/sh" to extract 7 files:
  35. #   README xtail.h xtail.c entryfuncs.c miscfuncs.c Makefile xtail.man
  36. # Wrapped by bin@vector on Wed Jul 26 19:18:07 CDT 1989
  37. # Unpacking this archive requires:  sed test wc (possibly mkdir)
  38. # Existing files will not be clobbered unless "-c" is specified on the cmd line.
  39. if test -f README -a "$1" != "-c" ; then
  40.     echo "README: file exists - will not be overwritten"
  41. else
  42.     echo "x - README (file 1 of 7, 1709 chars)"
  43.     sed -e 's/^X//' << 'END_OF_FILE_README' > README
  44. X"xtail" watches the growth of files.  It is similar to "tail -f", but may
  45. Xwatch many files at once.  The syntax is:
  46. X
  47. X    xtail pathname ...
  48. X    
  49. X"xtail" will monitor all the specified files and display information added
  50. Xto them.  If you specify a directory name, "xtail" will watch all the
  51. Xfiles in that directory - including those created after "xtail" was
  52. Xstarted.  If you give "xtail" a name which doesn't exist, it will watch
  53. Xfor the creation of the named entry.  My favorite usage is:
  54. X
  55. X    xtail /usr/spool/uucp/.Log/*
  56. X
  57. X"xtail" is distributed with a configuration for SCO XENIX.  It has also
  58. Xbeen tested on MIPS System V.  I took a shot at BSD portability.  The
  59. Xmain difference is how the "directory" support library is accessed.
  60. X
  61. XTo build "xtail":
  62. X
  63. X    - edit the definitions in "xtail.h"
  64. X    - run a "make"
  65. X
  66. XA version of "xtail" was originally posted in alt.sources a few months
  67. Xback.  There are several improvements between this version and the
  68. Xoriginal:
  69. X
  70. X       - the ability to watch directories
  71. X       - the ability to watch entries which don't exist yet
  72. X       - the recently changed files display (given upon SIGINT)
  73. X       - performance improvements
  74. X       - portability improvements
  75. X
  76. XMany of these changes were suggested by David Dykstra <dwd@cbnewsc.ATT.COM>.
  77. XThe idea of keeping files open and use fstat() rather than stat() was
  78. Xsuggested by changes by another poster (sorry, I lost the article so I
  79. Xcan't provide credit).  However, that version kept *everything* open, and
  80. Xthat just eats too many entries in the file table for me.  You can tweak
  81. Xthe values in "xtail.h" to optimize the response/load characteristics of
  82. X"xtail".
  83. X
  84. XChip Rosenthal
  85. X<chip@vector.Dallas.TX.US>
  86. X
  87. X@(#) README 2.1 89/07/26 19:16:34
  88. END_OF_FILE_README
  89.     size="`wc -c < README`"
  90.     if test 1709 -ne "$size" ; then
  91.     echo "README: extraction error - got $size chars"
  92.     fi
  93. fi
  94. if test -f xtail.h -a "$1" != "-c" ; then
  95.     echo "xtail.h: file exists - will not be overwritten"
  96. else
  97.     echo "x - xtail.h (file 2 of 7, 7187 chars)"
  98.     sed -e 's/^X//' << 'END_OF_FILE_xtail.h' > xtail.h
  99. X/*
  100. X * @(#) xtail.h 2.1 89/07/26 19:16:49
  101. X *
  102. X * Package:    xtail version 2
  103. X * File:    xtail.h
  104. X * Description:    header definitions
  105. X *
  106. X * Mon Jul 10 02:56:22 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
  107. X *    Original composition.
  108. X */
  109. X
  110. X
  111. X/*****************************************************************************
  112. X *
  113. X * Start of Site-Specific Customizations
  114. X *
  115. X *****************************************************************************/
  116. X
  117. X/*
  118. X * Define one of the following.  It says how to use your "directory" library.
  119. X */
  120. X#define DIR_XENIX    /* include <sys/ndir.h>, use "struct direct"    */
  121. X/*#define DIR_BSD    /* include <ndir.h>, use "struct direct"    */
  122. X/*#define DIR_SYSV    /* include <dirent.h>, use "struct dirent"    */
  123. X
  124. X/*
  125. X * Define one of the following.  It specifies the return type of "signal()".
  126. X */
  127. X#define SIGTYPE        int    /* declare as "int (*signal)()"        */
  128. X/*#define SIGTYPE    void    /* declare as "void (*signal)()"    */
  129. X
  130. X/*
  131. X * STATUS_ENAB    If defined, a SIGINT causes a summary of the opened files to
  132. X *        be displayed, and a SIGQUIT terminates the program.  If not
  133. X *        defined, these signals act normally.
  134. X */
  135. X#define STATUS_ENAB    /**/
  136. X
  137. X/*
  138. X * SLEEP_TIME    An iteration through the checking loop is performed once
  139. X *        per this many seconds.
  140. X */
  141. X#define SLEEP_TIME    1
  142. X
  143. X/*
  144. X * MAX_OPEN    This number of most recently changed files is kept open, and
  145. X *        they are checked every iteration through the checking loop.
  146. X *        Keeping these files open improves the performance because we
  147. X *        can use "fstat()" rather than "stat()".  Keeping too many
  148. X *        files open may overflow your open file table, and will reduce
  149. X *        performance by checking more files more frequently.
  150. X */
  151. X#define MAX_OPEN    6
  152. X
  153. X/*
  154. X * CHECK_COUNT    Everything besides open files are checked once per this
  155. X *        many iterations through the checking loop.
  156. X */
  157. X#define CHECK_COUNT    5
  158. X
  159. X/*
  160. X * MAX_ENTRIES    The maximum number of entries in any list.  It can be fairly
  161. X *        large -- each unused entry only eats 3*sizeof(char*) bytes.
  162. X */
  163. X#define MAX_ENTRIES    512
  164. X
  165. X
  166. X/*****************************************************************************
  167. X *
  168. X * End of Site-Specific Customizations
  169. X *
  170. X *****************************************************************************/
  171. X
  172. X
  173. X#define TRUE 1
  174. X#define FALSE 0
  175. X
  176. X#define Dprintf        if ( !Debug ) ; else (void) fprintf
  177. X
  178. X
  179. X/*
  180. X * Codes returned by the "stat_entry()" procedure.
  181. X */
  182. X#define ENTRY_ERROR    0    /* stat error or permissions error    */
  183. X#define ENTRY_SPECIAL    1    /* entry is a special file        */
  184. X#define ENTRY_FILE    2    /* entry is a regular file        */
  185. X#define ENTRY_DIR    3    /* entry is a directory            */
  186. X#define ENTRY_ZAP    4    /* specified entry doesn't exist    */
  187. X
  188. X
  189. X/*
  190. X * Diagnostic message codes.
  191. X *   The ordering of codes must correspond to the "mssg_list[]" defined below.
  192. X */
  193. X#define MSSG_NONE    0    /* no message - just reset header    */
  194. X#define MSSG_BANNER    1    /* display banner for file output    */
  195. X#define MSSG_CREATED    2    /* file has been created        */
  196. X#define MSSG_ZAPPED    3    /* file has been deleted        */
  197. X#define MSSG_TRUNC    4    /* file has been truncated        */
  198. X#define MSSG_NOTAFIL    5    /* error - not a regular file or dir    */
  199. X#define MSSG_STAT    6    /* error - stat() failed        */
  200. X#define MSSG_OPEN    7    /* error - open() failed        */
  201. X#define MSSG_SEEK    8    /* error - lseek() failed        */
  202. X#define MSSG_READ    9    /* error - read() failed        */
  203. X#define MSSG_UNKNOWN    10    /* unknown error - must be last in list */
  204. X
  205. X
  206. X#ifdef INTERN
  207. X#   define EXTERN
  208. X#else
  209. X#   define EXTERN extern
  210. X#endif
  211. X
  212. X
  213. X/*
  214. X * Each item we are watching is stored in a (struct entry_descrip).  These
  215. X * entries are placed in lists, which are managed as (struct entry_list).
  216. X *
  217. X * There are three lists maintained:
  218. X *
  219. X * List_file    All of the regular files we are watching.  We will try to
  220. X *        keep the MAX_OPEN most recently modified files open, and
  221. X *        they will be checked more frequently.
  222. X *
  223. X * List_dir    All of the directories we are watching.  If a file is created
  224. X *        in one of these directories, we will add it to "List_file".
  225. X *
  226. X * List_zap    All the entries which don't exist.  When something appears
  227. X *        under one of these names, the entry will be moved to either
  228. X *        "List_file" or "List_dir", as appropriate.
  229. X */
  230. X
  231. Xstruct entry_descrip {
  232. X    char *name;        /* pathname to the entry            */
  233. X    int fd;        /* opened fd, or <= 0 if not opened        */
  234. X    long size;        /* size of entry last time checked        */
  235. X    long mtime;        /* modification time last time checked        */
  236. X};
  237. X
  238. Xstruct entry_list {
  239. X    struct entry_descrip *list[MAX_ENTRIES];
  240. X    int num;
  241. X};
  242. X
  243. X/*
  244. X * The lists of entries being watched.
  245. X */
  246. XEXTERN struct entry_list List_file;    /* regular files        */
  247. XEXTERN struct entry_list List_dir;    /* directories            */
  248. XEXTERN struct entry_list List_zap;    /* nonexistent entries        */
  249. X
  250. X
  251. X/*
  252. X * List sorting status.
  253. X *   This flag indicates that "List_file" is sorted, and the right entries
  254. X *   are open.  Anything which possibly effects this state (e.g. an entry
  255. X *   is added to "List_file", the mtime of a file is changed, etc.) must set
  256. X *   this flag FALSE.  We will periodically check this flag and call the
  257. X *   "fixup_open_files()" procedure to resort and organize the list.
  258. X */
  259. XEXTERN int Sorted;
  260. X
  261. X
  262. X/*
  263. X * Entry status control flag.
  264. X *   The procedures which manipulate entries will reset the status information
  265. X *   if this flag is TRUE.  When initializing the lists we want this FALSE.
  266. X *   For example, consider the file size.  When initializing we want to use
  267. X *   the current file size, otherwise we would dump the file from the beginning.
  268. X *   However, later when we notice things are created we want to reset the
  269. X *   size to zero so that we do dump from the beginning.
  270. X */
  271. XEXTERN int Reset_status;
  272. X
  273. X
  274. X/*
  275. X * Debugging output flag.
  276. X */
  277. XEXTERN int Debug;
  278. X
  279. X
  280. X/*
  281. X * Diagnostic messages produced by the "message()" procedure.
  282. X *   The first "%s" is the entry name.  The second "%s" is the errno descrip.
  283. X */
  284. X#ifdef INTERN
  285. X    char *mssg_list[] = {
  286. X    NULL,                            /*MSSG_NONE   */
  287. X    "\n*** %s ***\n",                    /*MSSG_BANNER */
  288. X    "\n*** '%s' has been created ***\n",            /*MSSG_CREATED*/
  289. X    "\n*** '%s' has been deleted ***\n",            /*MSSG_ZAPPED */
  290. X    "\n*** '%s' has been truncated - rewinding ***\n",    /*MSSG_TRUNC  */
  291. X    "\n*** error - '%s' not a file or dir - removed ***\n",    /*MSSG_NOTAFIL*/
  292. X    "\n*** error - couldn't stat '%s' (%s) - removed ***\n",/*MSSG_STAT   */
  293. X    "\n*** error - couldn't open '%s' (%s) - removed ***\n",/*MSSG_OPEN   */
  294. X    "\n*** error - couldn't seek '%s' (%s) - removed ***\n",/*MSSG_SEEK   */
  295. X    "\n*** error - couldn't read '%s' (%s) - removed ***\n",/*MSSG_READ   */
  296. X    "\n*** error - unknown error on file '%s' ***\n",    /*MSSG_UNKNOWN*/
  297. X    };
  298. X#else
  299. X    extern char *mssg_list[];
  300. X#endif
  301. X
  302. X
  303. X/*
  304. X * Entry managment procedures.
  305. X */
  306. Xstruct entry_descrip *new_entry();    /* create a new entry and add to list */
  307. Xvoid move_entry();            /* move an entry between lists          */
  308. Xvoid rmv_entry();            /* remove an entry from a list          */
  309. Xint stat_entry();            /* get the inode status for an entry  */
  310. Xint open_entry();            /* open an entry              */
  311. X
  312. X/*
  313. X * Miscelaneous procedures.
  314. X */
  315. Xvoid fixup_open_files();        /* manage the open files          */
  316. Xint scan_directory();            /* scan a dir for files not on a list */
  317. Xvoid message();                /* standard message interface          */
  318. Xvoid show_status();            /* display currently opened files     */
  319. X
  320. END_OF_FILE_xtail.h
  321.     size="`wc -c < xtail.h`"
  322.     if test 7187 -ne "$size" ; then
  323.     echo "xtail.h: extraction error - got $size chars"
  324.     fi
  325. fi
  326. if test -f xtail.c -a "$1" != "-c" ; then
  327.     echo "xtail.c: file exists - will not be overwritten"
  328. else
  329.     echo "x - xtail.c (file 3 of 7, 8883 chars)"
  330.     sed -e 's/^X//' << 'END_OF_FILE_xtail.c' > xtail.c
  331. X/*
  332. X * @(#) xtail.c 2.1 89/07/26 19:15:42
  333. X *
  334. X * Package:    xtail version 2
  335. X * File:    xtail.c
  336. X * Description:    main program
  337. X *
  338. X * Mon Jul 10 02:56:22 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
  339. X *    Original composition.
  340. X */
  341. X
  342. X#ifndef LINT
  343. Xstatic char SCCSID[] = "@(#) xtail.c 2.1 89/07/26 19:15:42";
  344. X#endif
  345. X
  346. X#include <stdio.h>
  347. X#include <signal.h>
  348. X#include <sys/types.h>
  349. X#include <sys/stat.h>
  350. X#define  INTERN
  351. X#include "xtail.h"
  352. X
  353. X#ifdef M_XENIX
  354. X# undef  NULL
  355. X# define NULL 0
  356. X#endif
  357. X
  358. X
  359. Xint sigcaught = 0;
  360. X
  361. XSIGTYPE sigcatcher(sig)
  362. Xint sig;
  363. X{
  364. X    extern SIGTYPE (*signal)();
  365. X    if ( sig == SIGQUIT )
  366. X    (void) exit(0);
  367. X    sigcaught = sig;
  368. X#ifdef STATUS_ENAB
  369. X    (void) signal(SIGINT,sigcatcher);
  370. X    (void) signal(SIGQUIT,sigcatcher);
  371. X#endif
  372. X}
  373. X
  374. X
  375. Xmain(argc,argv)
  376. Xint argc;
  377. Xchar *argv[];
  378. X{
  379. X    int open_files_only, already_open, iteration, i;
  380. X    struct entry_descrip *entryp;
  381. X    struct stat sbuf;
  382. X
  383. X    /* 
  384. X     * Initialize.
  385. X     */
  386. X    List_file.num = 0;
  387. X    List_dir.num = 0;
  388. X    List_zap.num = 0;
  389. X    Sorted = FALSE;
  390. X    Reset_status = FALSE;
  391. X    Debug = FALSE;
  392. X    sigcatcher(0);
  393. X
  394. X
  395. X    /*
  396. X     * Place all of the entries onto lists.
  397. X     */
  398. X    for ( i = 1 ; i < argc ; ++i )  {
  399. X
  400. X    if ( i == 1 && strcmp(argv[i],"-D") == 0 ) {
  401. X        Debug = TRUE;
  402. X        continue;
  403. X    }
  404. X
  405. X    /*
  406. X     * Temporarily throw this entry onto the end of the zapped list.
  407. X     */
  408. X    entryp = new_entry( &List_zap, argv[i] );
  409. X
  410. X    /*
  411. X     * Stat the file and get it to its proper place.
  412. X     */
  413. X    switch ( stat_entry( &List_zap, List_zap.num-1, &sbuf ) ) {
  414. X
  415. X    case ENTRY_FILE:        /* move entry to file list    */
  416. X        move_entry( &List_file, &List_zap, List_zap.num-1 );
  417. X        entryp->size = sbuf.st_size;
  418. X        entryp->mtime = sbuf.st_mtime;
  419. X        break;
  420. X
  421. X    case ENTRY_DIR:            /* move entry to dir list    */
  422. X        move_entry( &List_dir, &List_zap, List_zap.num-1 );
  423. X        entryp->size = sbuf.st_size;
  424. X        entryp->mtime = sbuf.st_mtime;
  425. X        if ( scan_directory( entryp->name ) != 0 ) {
  426. X        message( MSSG_OPEN, entryp );
  427. X        rmv_entry( &List_dir, List_dir.num-1 );
  428. X        }
  429. X        break;
  430. X
  431. X    case ENTRY_ZAP:            /* keep entry on zap list    */
  432. X        break;
  433. X
  434. X    case ENTRY_SPECIAL:        /* entry is a special file    */
  435. X        message( MSSG_NOTAFIL, entryp );
  436. X        rmv_entry( &List_zap, List_zap.num-1 );
  437. X        break;
  438. X
  439. X    default:            /* stat error            */
  440. X        message( MSSG_STAT, entryp );
  441. X        rmv_entry( &List_zap, List_zap.num-1 );
  442. X        break;
  443. X
  444. X    }
  445. X
  446. X    }
  447. X
  448. X    /*
  449. X     * Make sure we are watching something reasonable.
  450. X     */
  451. X    if ( List_file.num == 0 ) {
  452. X    if ( List_dir.num == 0 && List_zap.num == 0 ) {
  453. X        (void) fprintf(stderr, "%s: no valid entries specified\n", argv[0]);
  454. X        (void) exit(1);
  455. X    }
  456. X    (void) puts("\n*** warning - no files are being watched ***");
  457. X    }
  458. X
  459. X
  460. X    /*
  461. X     * From this point on we want to reset the status of an entry any
  462. X     * time we move it around to another list.
  463. X     */
  464. X    Reset_status = TRUE;
  465. X
  466. X
  467. X    /*
  468. X     * Force a check of everything first time through the loop.
  469. X     */
  470. X    iteration = CHECK_COUNT;
  471. X
  472. X
  473. X    /* 
  474. X     * Loop forever.
  475. X     */
  476. X    for (;;) {
  477. X
  478. X    /*
  479. X     * Once every CHECK_COUNT iterations check everything.
  480. X     * All other times only look at the opened files.
  481. X     */
  482. X    open_files_only = ( ++iteration < CHECK_COUNT );
  483. X    if ( !open_files_only )
  484. X        iteration = 0;
  485. X
  486. X
  487. X    /*
  488. X     * Make sure that the most recently modified files are open.
  489. X     */
  490. X    if ( !Sorted )
  491. X        fixup_open_files();
  492. X
  493. X
  494. X    /*
  495. X     * Display what we are watching if a SIGINT was caught.
  496. X     */
  497. X    if ( sigcaught ) {
  498. X        show_status();
  499. X        sigcatcher(0);
  500. X    }
  501. X
  502. X
  503. X    /*
  504. X     * Go through all of the files looking for changes.
  505. X     */
  506. X    Dprintf(stderr, ">>> checking files list (%s)\n",
  507. X        ( open_files_only ? "open files only" : "all files" ));
  508. X    for ( i = 0 ; i < List_file.num ; ++i ) {
  509. X
  510. X        entryp = List_file.list[i];
  511. X        already_open = ( entryp->fd > 0 ) ;
  512. X
  513. X        /*
  514. X         * Ignore closed files except every CHECK_COUNT iterations.
  515. X         */
  516. X        if ( !already_open && open_files_only )
  517. X        continue;
  518. X
  519. X        /*
  520. X         * Get the status of this file.
  521. X         */
  522. X        switch ( stat_entry( &List_file, i, &sbuf ) ) {
  523. X        case ENTRY_FILE:        /* got status OK        */
  524. X        break;
  525. X        case ENTRY_DIR:        /* huh??? it's now a dir    */
  526. X        move_entry( &List_dir, &List_file, i-- );
  527. X        continue;
  528. X        case ENTRY_ZAP:        /* entry has been deleted    */
  529. X        message( MSSG_ZAPPED, entryp );
  530. X        move_entry( &List_zap, &List_file, i-- );
  531. X        continue;
  532. X        case ENTRY_SPECIAL:        /* entry is a special file    */
  533. X        message( MSSG_NOTAFIL, entryp );
  534. X        rmv_entry( &List_file, i-- );
  535. X        continue;
  536. X        default:            /* stat error            */
  537. X        message( MSSG_STAT, entryp );
  538. X        rmv_entry( &List_file, i-- );
  539. X        continue;
  540. X        }
  541. X
  542. X
  543. X        /*
  544. X         * See if an opened file has been deleted.
  545. X         */
  546. X        if ( already_open && sbuf.st_nlink == 0 ) {
  547. X        message( MSSG_ZAPPED, entryp );
  548. X        move_entry( &List_zap, &List_file, i-- );
  549. X        continue;
  550. X        }
  551. X
  552. X        /*
  553. X         * If nothing has changed then continue on.
  554. X         */
  555. X        if ( entryp->size==sbuf.st_size && entryp->mtime==sbuf.st_mtime )
  556. X        continue;
  557. X
  558. X        /*
  559. X         * If the file isn't already open, then do so.
  560. X         *   Note -- it is important that we call "fixup_open_files()"
  561. X         *   at the end of the loop to make sure too many files don't
  562. X         *   stay opened.
  563. X         */
  564. X        if ( !already_open && open_entry( &List_file, i ) != 0 ) {
  565. X        --i;
  566. X        continue;
  567. X        }
  568. X
  569. X        /*
  570. X         * See if the file has been truncated.
  571. X         */
  572. X        if ( sbuf.st_size < entryp->size ) {
  573. X        message( MSSG_TRUNC, entryp );
  574. X        entryp->size = 0;
  575. X        }
  576. X
  577. X        /*
  578. X         * Seek to where the changes begin.
  579. X         */
  580. X        {
  581. X        extern long lseek();
  582. X        if ( lseek( entryp->fd, entryp->size, 0 ) < 0 ) {
  583. X            message( MSSG_SEEK, entryp );
  584. X            rmv_entry( &List_file, i-- );
  585. X            continue;
  586. X        }
  587. X        }
  588. X
  589. X        /*
  590. X         * Dump the recently added info.
  591. X         */
  592. X        {
  593. X        int nb;
  594. X            static char buf[BUFSIZ];
  595. X        message( MSSG_BANNER, entryp );
  596. X        while ( ( nb = read( entryp->fd, buf, sizeof(buf) ) ) > 0 ) {
  597. X            (void) fwrite( buf, sizeof(char), (unsigned) nb, stdout );
  598. X            entryp->size += nb;
  599. X        }
  600. X        if ( nb < 0 ) {
  601. X            message( MSSG_READ, entryp );
  602. X            rmv_entry( &List_file, i-- );
  603. X            continue;
  604. X        }
  605. X        }
  606. X
  607. X        /*
  608. X         * Update the modification time.
  609. X         */
  610. X        entryp->mtime = sbuf.st_mtime;
  611. X
  612. X        /*
  613. X         * Since we've changed the mtime, the list might no longer be
  614. X         * sorted.  However if this entry is already at the top of the
  615. X         * list then it's OK.
  616. X         */
  617. X        if ( i != 0 )
  618. X        Sorted = FALSE;
  619. X
  620. X        /*
  621. X         * If we've just opened the file then force a resort now to
  622. X         * prevent too many files from being opened.
  623. X         */
  624. X        if ( !already_open )
  625. X        fixup_open_files();
  626. X
  627. X    }
  628. X
  629. X
  630. X    /*
  631. X     * Go through list of nonexistent entries to see if any have appeared.
  632. X     *   This is done only once every CHECK_COUNT iterations.
  633. X     */
  634. X    if ( !open_files_only ) {
  635. X        Dprintf(stderr, ">>> checking zapped list\n");
  636. X        for ( i = 0 ; i < List_zap.num ; ++i ) {
  637. X        entryp = List_zap.list[i];
  638. X        switch ( stat_entry( &List_zap, i, &sbuf ) ) {
  639. X        case ENTRY_FILE:    /* entry has appeared as a file    */
  640. X            message( MSSG_CREATED, entryp );
  641. X            move_entry( &List_file, &List_zap, i-- );
  642. X            break;
  643. X        case ENTRY_DIR:        /* entry has appeared as a dir    */
  644. X            message( MSSG_CREATED, entryp );
  645. X            move_entry( &List_dir, &List_zap, i-- );
  646. X            break;
  647. X        case ENTRY_ZAP:        /* entry still doesn't exist    */
  648. X            break;
  649. X        case ENTRY_SPECIAL:    /* entry is a special file    */
  650. X            message( MSSG_NOTAFIL, entryp );
  651. X                rmv_entry( &List_zap, i-- );
  652. X            break;
  653. X        default:        /* error - entry removed    */
  654. X                message( MSSG_STAT, entryp );
  655. X                rmv_entry( &List_zap, i-- );
  656. X            break;
  657. X        }
  658. X        }
  659. X    }
  660. X
  661. X
  662. X    /*
  663. X     * Go through the list of dirs to see if any new files were created.
  664. X     *   This is done only once every CHECK_COUNT iterations.
  665. X     */
  666. X    if ( !open_files_only ) {
  667. X        Dprintf(stderr, ">>> checking directory list\n");
  668. X        for ( i = 0 ; !open_files_only && i < List_dir.num ; ++i ) {
  669. X        entryp = List_dir.list[i];
  670. X        switch ( stat_entry( &List_dir, i, &sbuf ) ) {
  671. X        case ENTRY_DIR:        /* got status OK        */
  672. X            break;
  673. X        case ENTRY_FILE:    /* huh??? it's now a reg file    */
  674. X            move_entry( &List_file, &List_dir, i-- );
  675. X            continue;
  676. X        case ENTRY_ZAP:        /* entry has been deleted    */
  677. X                message( MSSG_ZAPPED, entryp );
  678. X                move_entry( &List_zap, &List_dir, i-- );
  679. X            continue;
  680. X        case ENTRY_SPECIAL:    /* entry is a special file    */
  681. X            message( MSSG_NOTAFIL, entryp );
  682. X            rmv_entry( &List_dir, i-- );
  683. X            continue;
  684. X        default:        /* stat error            */
  685. X            message( MSSG_STAT, entryp );
  686. X            rmv_entry( &List_dir, i-- );
  687. X            continue;
  688. X        }
  689. X        if ( entryp->mtime == sbuf.st_mtime )
  690. X            continue;
  691. X        if ( scan_directory( entryp->name ) != 0 ) {
  692. X            message( MSSG_OPEN, entryp );
  693. X            rmv_entry( &List_dir, i-- );
  694. X        }
  695. X        entryp->mtime = sbuf.st_mtime;
  696. X        }
  697. X    }
  698. X
  699. X
  700. X    /*
  701. X     * End of checking loop.
  702. X     */
  703. X    {
  704. X        extern unsigned sleep();
  705. X        (void) fflush(stdout);
  706. X        (void) sleep(SLEEP_TIME);
  707. X    }
  708. X
  709. X    }
  710. X
  711. X    /*NOTREACHED*/
  712. X
  713. X}
  714. X
  715. END_OF_FILE_xtail.c
  716.     size="`wc -c < xtail.c`"
  717.     if test 8883 -ne "$size" ; then
  718.     echo "xtail.c: extraction error - got $size chars"
  719.     fi
  720. fi
  721. if test -f entryfuncs.c -a "$1" != "-c" ; then
  722.     echo "entryfuncs.c: file exists - will not be overwritten"
  723. else
  724.     echo "x - entryfuncs.c (file 4 of 7, 5152 chars)"
  725.     sed -e 's/^X//' << 'END_OF_FILE_entryfuncs.c' > entryfuncs.c
  726. X/*
  727. X * @(#) entryfuncs.c 2.1 89/07/26 19:16:49
  728. X *
  729. X * Package:    xtail version 2
  730. X * File:    entryfuncs.c
  731. X * Description:    procedures to manage individual entries
  732. X *
  733. X * Mon Jul 10 02:56:22 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
  734. X *    Original composition.
  735. X */
  736. X
  737. X#ifndef LINT
  738. Xstatic char SCCSID[] = "@(#) entryfuncs.c 2.1 89/07/26 19:16:49";
  739. X#endif
  740. X
  741. X#include <stdio.h>
  742. X#include <fcntl.h>
  743. X#include <sys/types.h>
  744. X#include <sys/stat.h>
  745. X#include <sys/errno.h>
  746. X#include "xtail.h"
  747. X
  748. X#ifdef M_XENIX
  749. X# undef  NULL
  750. X# define NULL 0
  751. X#endif
  752. X
  753. Xextern int errno;
  754. X
  755. X
  756. Xstatic struct entry_descrip *E_append(listp,entryp)
  757. Xstruct entry_list *listp;
  758. Xstruct entry_descrip *entryp;
  759. X{
  760. X    if ( listp->num >= MAX_ENTRIES ) {
  761. X    (void) fprintf(stderr,"%s: too many entries (%d max)\n",
  762. X        entryp->name, MAX_ENTRIES);
  763. X    (void) exit(2);
  764. X    }
  765. X    listp->list[listp->num++] = entryp;
  766. X    Sorted = FALSE;
  767. X    return entryp;
  768. X}
  769. X
  770. X
  771. Xstatic void E_remove(listp,entryno)
  772. Xstruct entry_list *listp;
  773. Xint entryno;
  774. X{
  775. X    while ( ++entryno < listp->num )
  776. X    listp->list[entryno-1] = listp->list[entryno];
  777. X    --listp->num;
  778. X    Sorted = FALSE;
  779. X}
  780. X
  781. X
  782. Xstatic char *list_name(listp)        /* for debug output only */
  783. Xstruct entry_list *listp;
  784. X{
  785. X    if ( listp == &List_file )    return "<file>";
  786. X    if ( listp == &List_dir )    return "<dir>";
  787. X    if ( listp == &List_zap )    return "<zap>";
  788. X    return "?unknown?";
  789. X}
  790. X
  791. X
  792. X/*
  793. X * Create a new entry description and append it to a list.
  794. X */
  795. Xstruct entry_descrip *new_entry(listp,name)
  796. Xstruct entry_list *listp;
  797. Xchar *name;
  798. X{
  799. X    struct entry_descrip *entryp;
  800. X    static char malloc_error[] = "malloc: out of space\n";
  801. X    extern char *strcpy(), *malloc();
  802. X
  803. X    Dprintf(stderr, ">>> creating entry '%s' on %s list\n",
  804. X    name, list_name(listp));
  805. X
  806. X    entryp = (struct entry_descrip *) malloc( sizeof(struct entry_descrip) );
  807. X    if ( entryp == NULL ) {
  808. X    (void) fputs(malloc_error,stderr);
  809. X    (void) exit(2);
  810. X    }
  811. X
  812. X    entryp->name = malloc( (unsigned) strlen(name) + 1 );
  813. X    if ( entryp->name == NULL ) {
  814. X    (void) fputs(malloc_error,stderr);
  815. X    (void) exit(2);
  816. X    }
  817. X    (void) strcpy(entryp->name,name);
  818. X
  819. X    entryp->fd = 0;
  820. X    entryp->size =  0;
  821. X    entryp->mtime = 0;
  822. X
  823. X    return E_append(listp,entryp);
  824. X}
  825. X
  826. X
  827. X/*
  828. X * Remove an entry from a list and free up its space.
  829. X */
  830. Xvoid rmv_entry(listp,entryno)
  831. Xstruct entry_list *listp;
  832. Xint entryno;
  833. X{
  834. X    struct entry_descrip *entryp = listp->list[entryno];
  835. X    extern void free();
  836. X
  837. X    Dprintf(stderr, ">>> removing entry '%s' from %s list\n",
  838. X    listp->list[entryno]->name, list_name(listp));
  839. X    E_remove(listp,entryno);
  840. X    if ( entryp->fd > 0 )
  841. X    (void) close(entryp->fd);
  842. X    free( entryp->name );
  843. X    free( (char *) entryp );
  844. X}
  845. X
  846. X
  847. X/*
  848. X * Move an entry from one list to another.
  849. X *    In addition we close up the entry if appropriate.
  850. X */
  851. Xvoid move_entry(dst_listp,src_listp,src_entryno)
  852. Xstruct entry_list *dst_listp;
  853. Xstruct entry_list *src_listp;
  854. Xint src_entryno;
  855. X{
  856. X    struct entry_descrip *entryp = src_listp->list[src_entryno];
  857. X
  858. X    Dprintf(stderr, ">>> moving entry '%s' from %s list to %s list\n",
  859. X    src_listp->list[src_entryno]->name,
  860. X    list_name(src_listp), list_name(dst_listp));
  861. X    if ( entryp->fd > 0 ) {
  862. X    (void) close(entryp->fd);
  863. X    entryp->fd = 0;
  864. X    }
  865. X    E_remove(src_listp,src_entryno);
  866. X    (void) E_append(dst_listp,entryp);
  867. X    if ( Reset_status ) {
  868. X    entryp->size = 0;
  869. X    entryp->mtime = 0;
  870. X    }
  871. X}
  872. X
  873. X
  874. X/*
  875. X * Get the inode status for an entry.
  876. X *    Returns code describing the status of the entry.
  877. X */
  878. Xint stat_entry(listp,entryno,sbuf)
  879. Xstruct entry_list *listp;
  880. Xint entryno;
  881. Xregister struct stat *sbuf;
  882. X{
  883. X    register int status;
  884. X    register struct entry_descrip *entryp = listp->list[entryno];
  885. X    static int my_gid = -1;
  886. X    static int my_uid = -1;
  887. X
  888. X    if ( my_gid < 0 ) {
  889. X    my_gid = getegid();
  890. X    my_uid = geteuid();
  891. X    }
  892. X
  893. X    status = 
  894. X    ( entryp->fd > 0 ? fstat(entryp->fd,sbuf) : stat(entryp->name,sbuf) );
  895. X
  896. X    if ( status != 0 )
  897. X    return ( errno == ENOENT ? ENTRY_ZAP : ENTRY_ERROR );
  898. X
  899. X    if (
  900. X    ( ( sbuf->st_mode & 0004 ) == 0 ) &&
  901. X    ( ( sbuf->st_mode & 0040 ) == 0 || sbuf->st_gid != my_gid ) &&
  902. X    ( ( sbuf->st_mode & 0400 ) == 0 || sbuf->st_uid != my_uid )
  903. X    ) {
  904. X    errno = EACCES;
  905. X    return ENTRY_ERROR;
  906. X    }
  907. X
  908. X    switch ( sbuf->st_mode & S_IFMT ) {
  909. X    case S_IFREG:    return ENTRY_FILE;
  910. X    case S_IFDIR:    return ENTRY_DIR;
  911. X    default:    return ENTRY_SPECIAL;
  912. X    }
  913. X
  914. X    /*NOTREACHED*/
  915. X}
  916. X
  917. X
  918. X/*
  919. X * Open an entry.
  920. X *    Returns 0 if the open is successful, else returns errno.  In the case
  921. X *    of an error, an appropriate diagnostic will be printed, and the entry
  922. X *    will be moved or deleted as required.  If the entry is already opened,
  923. X *    then no action will occur and 0 will be returned.
  924. X */
  925. Xint open_entry(listp,entryno)
  926. Xstruct entry_list *listp;
  927. Xint entryno;
  928. X{
  929. X    struct entry_descrip *entryp = listp->list[entryno];
  930. X
  931. X    if ( entryp->fd > 0 )
  932. X    return 0;
  933. X
  934. X    Dprintf(stderr, ">>> opening entry '%s' on %s list\n",
  935. X    listp->list[entryno]->name, list_name(listp));
  936. X    if ( (entryp->fd=open(entryp->name,O_RDONLY)) > 0 )
  937. X    return 0;
  938. X
  939. X    if ( errno == ENOENT ) {
  940. X    message( MSSG_ZAPPED, entryp );
  941. X    move_entry( &List_zap, listp, entryno );
  942. X    } else {
  943. X    message( MSSG_OPEN, entryp );
  944. X    rmv_entry( listp, entryno );
  945. X    }
  946. X    return -1;
  947. X}
  948. X
  949. X
  950. END_OF_FILE_entryfuncs.c
  951.     size="`wc -c < entryfuncs.c`"
  952.     if test 5152 -ne "$size" ; then
  953.     echo "entryfuncs.c: extraction error - got $size chars"
  954.     fi
  955. fi
  956. if test -f miscfuncs.c -a "$1" != "-c" ; then
  957.     echo "miscfuncs.c: file exists - will not be overwritten"
  958. else
  959.     echo "x - miscfuncs.c (file 5 of 7, 5423 chars)"
  960.     sed -e 's/^X//' << 'END_OF_FILE_miscfuncs.c' > miscfuncs.c
  961. X/*
  962. X * @(#) miscfuncs.c 2.1 89/07/26 19:16:50
  963. X *
  964. X * Package:    xtail version 2
  965. X * File:    miscfuncs.c
  966. X * Description:    miscelaneous support procedures
  967. X *
  968. X * Mon Jul 10 02:56:22 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
  969. X *    Original composition.
  970. X */
  971. X
  972. X#ifndef LINT
  973. Xstatic char SCCSID[] = "@(#) miscfuncs.c 2.1 89/07/26 19:16:50";
  974. X#endif
  975. X
  976. X#include <stdio.h>
  977. X#include <fcntl.h>
  978. X#include <time.h>
  979. X#include <sys/types.h>
  980. X#include <sys/stat.h>
  981. X#include "xtail.h"
  982. X
  983. X#ifdef M_XENIX
  984. X# undef  NULL
  985. X# define NULL 0
  986. X#endif
  987. X
  988. X/*
  989. X * How come the portable directory routines are so !$*&@# unportable?
  990. X */
  991. X#ifdef DIR_XENIX
  992. X#   include <sys/ndir.h>
  993. X    typedef struct direct DIRENT;
  994. X#endif
  995. X#ifdef DIR_BSD
  996. X#   include <ndir.h>
  997. X    typedef struct direct DIRENT;
  998. X#endif
  999. X#ifdef DIR_SYSV
  1000. X#   include <dirent.h>
  1001. X    typedef struct dirent DIRENT;
  1002. X#endif
  1003. X
  1004. Xextern int errno;
  1005. Xextern char *sys_errlist[];
  1006. X
  1007. X
  1008. X/*
  1009. X * Scan a directory for files not currently on a list.
  1010. X */
  1011. Xint scan_directory(dirname)
  1012. Xchar *dirname;
  1013. X{
  1014. X    register int i;
  1015. X    register DIRENT *dp;
  1016. X    register struct entry_descrip **elist, *entryp;
  1017. X    char *basename;
  1018. X    struct stat sbuf;
  1019. X    DIR *dirp;
  1020. X    static char pathname[MAXNAMLEN];
  1021. X    extern char *strcpy(), *strcat();
  1022. X
  1023. X    Dprintf(stderr, ">>> scanning directory '%s'\n", dirname);
  1024. X    if ( (dirp=opendir(dirname)) == NULL )
  1025. X    return -1;
  1026. X
  1027. X    (void) strcat( strcpy(pathname,dirname), "/" );
  1028. X    basename = pathname + strlen(pathname);
  1029. X
  1030. X#define SKIP_DIR(D) \
  1031. X    ( D[0] == '.' && ( D[1] == '\0' || ( D[1] == '.' && D[2] == '\0' ) ) )
  1032. X
  1033. X    while ( (dp=readdir(dirp)) != NULL ) {
  1034. X
  1035. X    if ( SKIP_DIR(dp->d_name) )
  1036. X        continue;
  1037. X    (void) strcpy( basename, dp->d_name );
  1038. X    if ( stat(pathname,&sbuf) != 0 )
  1039. X        continue;
  1040. X    if ( (sbuf.st_mode&S_IFMT) != S_IFREG )
  1041. X        continue;
  1042. X
  1043. X    for ( i=List_file.num, elist=List_file.list ; i > 0 ; --i, ++elist ) {
  1044. X        if ( strcmp( (*elist)->name, pathname ) == 0 )
  1045. X        break;
  1046. X    }
  1047. X    if ( i > 0 )
  1048. X        continue;
  1049. X
  1050. X    for ( i=List_zap.num, elist=List_zap.list ; i > 0 ; --i, ++elist ) {
  1051. X        if ( strcmp( (*elist)->name, pathname ) == 0 )
  1052. X        break;
  1053. X    }
  1054. X    if ( i > 0 )
  1055. X        continue;
  1056. X
  1057. X    entryp = new_entry( &List_file, pathname );
  1058. X    if ( Reset_status ) {
  1059. X        message( MSSG_CREATED, entryp );
  1060. X    } else {
  1061. X        entryp->mtime = sbuf.st_mtime;
  1062. X        entryp->size = sbuf.st_size;
  1063. X    }
  1064. X
  1065. X    }
  1066. X
  1067. X    (void) closedir(dirp);
  1068. X    return 0;
  1069. X
  1070. X}
  1071. X
  1072. X
  1073. X/*
  1074. X * Compare mtime of two entries.  Used by the "qsort()" in "fixup_open_files()".
  1075. X */
  1076. Xstatic int ecmp(ep1,ep2)
  1077. Xregister struct entry_descrip **ep1, **ep2;
  1078. X{
  1079. X    return ( (*ep2)->mtime - (*ep1)->mtime );
  1080. X}
  1081. X
  1082. X/*
  1083. X * Manage the open files.
  1084. X *   A small number of entries in "List_file" are kept open to minimize
  1085. X *   the overhead in checking for changes.  The strategy is to make sure
  1086. X *   the MAX_OPEN most recently modified files are all open.
  1087. X */
  1088. Xvoid fixup_open_files()
  1089. X{
  1090. X    register int i;
  1091. X    register struct entry_descrip **elist;
  1092. X    extern void qsort();
  1093. X
  1094. X    Dprintf(stderr, ">>> resorting file list\n");
  1095. X    (void) qsort(
  1096. X    (char *) List_file.list,
  1097. X    List_file.num,
  1098. X    sizeof(struct entry_descrip *),
  1099. X    ecmp
  1100. X    );
  1101. X    Sorted = TRUE;
  1102. X
  1103. X    /*
  1104. X     * Start at the end of the list.
  1105. X     */
  1106. X    i = List_file.num - 1;
  1107. X    elist = &List_file.list[i];
  1108. X
  1109. X    /*
  1110. X     * All the files at the end of the list should be closed.
  1111. X     */
  1112. X    for ( ; i >= MAX_OPEN ; --i, --elist ) {
  1113. X    if ( (*elist)->fd > 0 ) {
  1114. X        (void) close( (*elist)->fd );
  1115. X        (*elist)->fd = 0;
  1116. X    }
  1117. X    }
  1118. X
  1119. X    /*
  1120. X     * The first MAX_OPEN files in the list should be open.
  1121. X     */
  1122. X    for ( ; i >= 0 ; --i, --elist ) {
  1123. X    if ( (*elist)->fd <= 0 )
  1124. X        (void) open_entry( &List_file, i );
  1125. X    }
  1126. X
  1127. X}
  1128. X
  1129. X
  1130. X/*
  1131. X * Standard message interface.
  1132. X *   There are two reasons for this message interface.  First, it provides
  1133. X *   consistent diagnostics for all the messages.  Second, it manages the
  1134. X *   filename banner display whenever we switch to a different file.
  1135. X *   Warning - "errno" is used in some of the messages, so care must be
  1136. X *   taken not to step on it before message() can be called.
  1137. X */
  1138. Xvoid message(sel,e)
  1139. Xint sel;
  1140. Xstruct entry_descrip *e;
  1141. X{
  1142. X    static char *ofile = NULL;
  1143. X
  1144. X    /*
  1145. X     * Don't display the file banner if the file hasn't changed since last time.
  1146. X     */
  1147. X    if ( sel == MSSG_BANNER && ofile != NULL && strcmp(ofile,e->name) == 0 )
  1148. X    return;
  1149. X
  1150. X    /*
  1151. X     * Make sure the message selector is within range.
  1152. X     */
  1153. X    if ( sel < 0 || sel > MSSG_UNKNOWN )
  1154. X    sel = MSSG_UNKNOWN;
  1155. X
  1156. X    /*
  1157. X     * Display the message.
  1158. X     */
  1159. X    if ( mssg_list[sel] != NULL )
  1160. X    (void) printf(mssg_list[sel], e->name, sys_errlist[errno]);
  1161. X
  1162. X    ofile = ( sel == MSSG_BANNER ? e->name : NULL );
  1163. X}
  1164. X
  1165. X
  1166. X/*
  1167. X * Display currently opened files.
  1168. X */
  1169. Xvoid show_status()
  1170. X{
  1171. X    int i, n;
  1172. X    struct tm *tp;
  1173. X    static char *monname[] = {
  1174. X    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1175. X    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  1176. X    };
  1177. X    extern struct tm *localtime();
  1178. X
  1179. X    (void) printf("\n*** recently changed files ***\n");
  1180. X    for ( i = 0, n = 0 ; i < List_file.num ; ++i ) {
  1181. X    if ( List_file.list[i]->fd > 0 ) {
  1182. X        tp = localtime(&List_file.list[i]->mtime);
  1183. X        (void) printf("%4d  %2d-%3s-%02d %02d:%02d:%02d  %s\n",
  1184. X        ++n,
  1185. X        tp->tm_mday, monname[tp->tm_mon], tp->tm_year,
  1186. X        tp->tm_hour, tp->tm_min, tp->tm_sec,
  1187. X        List_file.list[i]->name
  1188. X        );
  1189. X    }
  1190. X    }
  1191. X
  1192. X    (void) printf( 
  1193. X    "currently watching:  %d files  %d dirs  %d unknown entries\n",
  1194. X    List_file.num, List_dir.num, List_zap.num);
  1195. X
  1196. X    message( MSSG_NONE, (struct entry_descrip *) NULL  );
  1197. X
  1198. X}
  1199. X
  1200. END_OF_FILE_miscfuncs.c
  1201.     size="`wc -c < miscfuncs.c`"
  1202.     if test 5423 -ne "$size" ; then
  1203.     echo "miscfuncs.c: extraction error - got $size chars"
  1204.     fi
  1205. fi
  1206. if test -f Makefile -a "$1" != "-c" ; then
  1207.     echo "Makefile: file exists - will not be overwritten"
  1208. else
  1209.     echo "x - Makefile (file 6 of 7, 2046 chars)"
  1210.     sed -e 's/^X//' << 'END_OF_FILE_Makefile' > Makefile
  1211. X
  1212. X# @(#) Makefile 2.1 89/07/26 19:15:39
  1213. X# Makefile for "xtail" (generated by /local/bin/makemake version 1.00.07)
  1214. X# Created by bin@vector on Wed Jul 26 17:36:37 CDT 1989
  1215. X
  1216. XSHELL = /bin/sh
  1217. XCC = cc
  1218. XDEFS = 
  1219. XCOPTS = -O
  1220. XLOPTS = 
  1221. XLIBS = -lx
  1222. XDEBUG = -g -DDEBUG 
  1223. XLINTFLAGS = -DLINT
  1224. X
  1225. XTARG = xtail
  1226. XOTHERS = 
  1227. X
  1228. XSRCS = xtail.c entryfuncs.c miscfuncs.c
  1229. X
  1230. XOBJS = xtail.o entryfuncs.o miscfuncs.o
  1231. X
  1232. X# Any edits below this line will be lost if "makemake" is rerun!
  1233. X# Commands may be inserted after the '#%custom' line at the end of this file.
  1234. X
  1235. XCFLAGS = $(COPTS) $(DEFS) # $(DEBUG)
  1236. XLFLAGS = $(LOPTS) # $(DEBUG)
  1237. X
  1238. Xall:        $(TARG) $(OTHERS)
  1239. Xinstall:    all        ; inst Install
  1240. Xclean:                ; rm -f $(TARG) $(OBJS) a.out core $(TARG).lint
  1241. Xclobber:    clean        ; inst -u Install
  1242. Xlint:        $(TARG).lint
  1243. X
  1244. X$(TARG):        $(OBJS)
  1245. X        $(CC) $(LFLAGS) -o $@ $(OBJS) $(LIBS)
  1246. X
  1247. X$(TARG).lint:    $(TARG)
  1248. X        lint $(LINTFLAGS) $(DEFS) $(SRCS) $(LIBS) > $@
  1249. X
  1250. Xxtail.o: /usr/include/signal.h /usr/include/stdio.h /usr/include/sys/signal.h \
  1251. X        /usr/include/sys/stat.h /usr/include/sys/types.h xtail.c \
  1252. X        xtail.h
  1253. Xentryfuncs.o: /usr/include/fcntl.h /usr/include/stdio.h \
  1254. X        /usr/include/sys/errno.h /usr/include/sys/fcntl.h \
  1255. X        /usr/include/sys/lockcmn.h /usr/include/sys/stat.h \
  1256. X        /usr/include/sys/types.h entryfuncs.c xtail.h
  1257. Xmiscfuncs.o: /usr/include/fcntl.h /usr/include/stdio.h \
  1258. X        /usr/include/sys/fcntl.h /usr/include/sys/lockcmn.h \
  1259. X        /usr/include/sys/ndir.h /usr/include/sys/stat.h \
  1260. X        /usr/include/sys/types.h /usr/include/time.h miscfuncs.c \
  1261. X        xtail.h
  1262. X
  1263. Xmake:        ;
  1264. X        /local/bin/makemake -i -v1.00.07 -aMakefile \
  1265. X            -DSHELL='$(SHELL)' -DCC='$(CC)' -DDEFS='$(DEFS)' \
  1266. X            -DCOPTS='$(COPTS)' -DLOPTS='$(LOPTS)' -DLIBS='$(LIBS)' \
  1267. X            -DDEBUG='$(DEBUG)' -DLINTFLAGS='$(LINTFLAGS)' \
  1268. X            -DOTHERS='$(OTHERS)' $(TARG) $(SRCS)
  1269. X
  1270. X#%custom - commands below this line will be maintained if 'makemake' is rerun
  1271. X
  1272. XARLIST = README xtail.h xtail.c entryfuncs.c miscfuncs.c Makefile xtail.man
  1273. X
  1274. Xshar:        xtail.shar
  1275. Xxtail.shar:    $(ARLIST)    ; shar $(ARLIST) > xtail.shar
  1276. X
  1277. END_OF_FILE_Makefile
  1278.     size="`wc -c < Makefile`"
  1279.     if test 2046 -ne "$size" ; then
  1280.     echo "Makefile: extraction error - got $size chars"
  1281.     fi
  1282. fi
  1283. if test -f xtail.man -a "$1" != "-c" ; then
  1284.     echo "xtail.man: file exists - will not be overwritten"
  1285. else
  1286.     echo "x - xtail.man (file 7 of 7, 1300 chars)"
  1287.     sed -e 's/^X//' << 'END_OF_FILE_xtail.man' > xtail.man
  1288. X''' @(#) xtail.man 2.1 89/07/26 19:15:44
  1289. X.TH XTAIL 1L
  1290. X.SH NAME
  1291. Xxtail - Watch the growth of files.
  1292. X.SH SYNTAX
  1293. X.B xtail
  1294. Xentry ...
  1295. X.SH DESCRIPTION
  1296. X.I Xtail
  1297. Xmonitors one or more files, and displays all data written to a file
  1298. Xsince command invocation.  It is very useful for monitoring multiple
  1299. Xlogfiles simultaneously.
  1300. X.P
  1301. XIf an
  1302. X.I entry
  1303. Xgiven on the command line is a directory, all files in that directory
  1304. Xwill be monitored, including those created after the
  1305. X.I xtail
  1306. Xinvocation.  If an
  1307. X.I entry
  1308. Xgiven on the command line doesn't exist,
  1309. X.I xtail
  1310. Xwill watch for it and monitor it once created.  When switching files in
  1311. Xthe display, a banner showing the pathname of the file is printed.
  1312. X.P
  1313. XAn interrupt character (usually CTRL/C or DEL) will display a list of the
  1314. Xmost recently modified files being watched.  Send a quit signal
  1315. X(usually CTRL/backslash) to stop
  1316. X.IR xtail .
  1317. X.SH SEE ALSO
  1318. Xtail(1)
  1319. X.SH NOTES
  1320. X.I Xtail
  1321. Xmay be easily confused.  For example, if a file is renamed,
  1322. X.I xtail
  1323. Xmay or may not continue to monitor it.  If you ask it to monitor a file
  1324. Xmultiple times, it probably will.  If you misspell a filename,
  1325. X.I xtail
  1326. Xwill treat it as a nonexistent entry and happily wait for its creation.
  1327. X.P
  1328. XMy favorite use is "xtail /usr/spool/uucp/.Log/*".
  1329. X.SH AUTHOR
  1330. XChip Rosenthal <chip@vector.Dallas.TX.US>
  1331. END_OF_FILE_xtail.man
  1332.     size="`wc -c < xtail.man`"
  1333.     if test 1300 -ne "$size" ; then
  1334.     echo "xtail.man: extraction error - got $size chars"
  1335.     fi
  1336. fi
  1337. echo "done - 7 files extracted"
  1338. exit 0
  1339. --- cut here -----------------------------------------------------------------
  1340. -- 
  1341. Chip Rosenthal / chip@vector.Dallas.TX.US / Dallas Semiconductor / 214-450-5337
  1342. "I wish you'd put that starvation box down and go to bed" - Albert Collins' Mom
  1343.  
  1344.  
  1345.