1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "prio.h"
7 #include "prprf.h"
8 #include "prinit.h"
9 #include "prthread.h"
10 #include "prinrval.h"
11 
12 #include "plerror.h"
13 #include "plgetopt.h"
14 
15 #include <stdlib.h>
16 
17 #define BUFFER_SIZE 500
18 
19 static PRFileDesc *out = NULL, *err = NULL;
20 
Help(void)21 static void Help(void)
22 {
23     PR_fprintf(err, "Usage: tail [-n <n>] [-f] [-h] <filename>\n");
24     PR_fprintf(err, "\t-t <n>	Dally time in milliseconds\n");
25     PR_fprintf(err, "\t-n <n>	Number of bytes before <eof>\n");
26     PR_fprintf(err, "\t-f   	Follow the <eof>\n");
27     PR_fprintf(err, "\t-h   	This message and nothing else\n");
28 }  /* Help */
29 
main(PRIntn argc,char ** argv)30 PRIntn main(PRIntn argc, char **argv)
31 {
32     PRIntn rv = 0;
33     PLOptStatus os;
34     PRStatus status;
35     PRFileDesc *file;
36     PRFileInfo fileInfo;
37     PRIntervalTime dally;
38     char buffer[BUFFER_SIZE];
39     PRBool follow = PR_FALSE;
40     const char *filename = NULL;
41     PRUint32 position = 0, seek = 0, time = 0;
42     PLOptState *opt = PL_CreateOptState(argc, argv, "hfn:");
43 
44     out = PR_GetSpecialFD(PR_StandardOutput);
45     err = PR_GetSpecialFD(PR_StandardError);
46 
47     while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
48     {
49         if (PL_OPT_BAD == os) {
50             continue;
51         }
52         switch (opt->option)
53         {
54             case 0:  /* it's the filename */
55                 filename = opt->value;
56                 break;
57             case 'n':  /* bytes before end of file */
58                 seek = atoi(opt->value);
59                 break;
60             case 't':  /* dally time */
61                 time = atoi(opt->value);
62                 break;
63             case 'f':  /* follow the end of file */
64                 follow = PR_TRUE;
65                 break;
66             case 'h':  /* user wants some guidance */
67                 Help();  /* so give him an earful */
68                 return 2;  /* but not a lot else */
69                 break;
70             default:
71                 break;
72         }
73     }
74     PL_DestroyOptState(opt);
75 
76     if (0 == time) {
77         time = 1000;
78     }
79     dally = PR_MillisecondsToInterval(time);
80 
81     if (NULL == filename)
82     {
83         (void)PR_fprintf(out, "Input file not specified\n");
84         rv = 1; goto done;
85     }
86     file = PR_Open(filename, PR_RDONLY, 0);
87     if (NULL == file)
88     {
89         PL_FPrintError(err, "File cannot be opened for reading");
90         return 1;
91     }
92 
93     status = PR_GetOpenFileInfo(file, &fileInfo);
94     if (PR_FAILURE == status)
95     {
96         PL_FPrintError(err, "Cannot acquire status of file");
97         rv = 1; goto done;
98     }
99     if (seek > 0)
100     {
101         if (seek > fileInfo.size) {
102             seek = 0;
103         }
104         position = PR_Seek(file, (fileInfo.size - seek), PR_SEEK_SET);
105         if (-1 == (PRInt32)position) {
106             PL_FPrintError(err, "Cannot seek to starting position");
107         }
108     }
109 
110     do
111     {
112         while (position < fileInfo.size)
113         {
114             PRInt32 read, bytes = fileInfo.size - position;
115             if (bytes > sizeof(buffer)) {
116                 bytes = sizeof(buffer);
117             }
118             read = PR_Read(file, buffer, bytes);
119             if (read != bytes) {
120                 PL_FPrintError(err, "Cannot read to eof");
121             }
122             position += read;
123             PR_Write(out, buffer, read);
124         }
125 
126         if (follow)
127         {
128             PR_Sleep(dally);
129             status = PR_GetOpenFileInfo(file, &fileInfo);
130             if (PR_FAILURE == status)
131             {
132                 PL_FPrintError(err, "Cannot acquire status of file");
133                 rv = 1; goto done;
134             }
135         }
136     } while (follow);
137 
138 done:
139     PR_Close(file);
140 
141     return rv;
142 }  /* main */
143 
144 /* tail.c */
145