|
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 |
|
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 |
|
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) continue; |
|
50 switch (opt->option) |
|
51 { |
|
52 case 0: /* it's the filename */ |
|
53 filename = opt->value; |
|
54 break; |
|
55 case 'n': /* bytes before end of file */ |
|
56 seek = atoi(opt->value); |
|
57 break; |
|
58 case 't': /* dally time */ |
|
59 time = atoi(opt->value); |
|
60 break; |
|
61 case 'f': /* follow the end of file */ |
|
62 follow = PR_TRUE; |
|
63 break; |
|
64 case 'h': /* user wants some guidance */ |
|
65 Help(); /* so give him an earful */ |
|
66 return 2; /* but not a lot else */ |
|
67 break; |
|
68 default: |
|
69 break; |
|
70 } |
|
71 } |
|
72 PL_DestroyOptState(opt); |
|
73 |
|
74 if (0 == time) time = 1000; |
|
75 dally = PR_MillisecondsToInterval(time); |
|
76 |
|
77 if (NULL == filename) |
|
78 { |
|
79 (void)PR_fprintf(out, "Input file not specified\n"); |
|
80 rv = 1; goto done; |
|
81 } |
|
82 file = PR_Open(filename, PR_RDONLY, 0); |
|
83 if (NULL == file) |
|
84 { |
|
85 PL_FPrintError(err, "File cannot be opened for reading"); |
|
86 return 1; |
|
87 } |
|
88 |
|
89 status = PR_GetOpenFileInfo(file, &fileInfo); |
|
90 if (PR_FAILURE == status) |
|
91 { |
|
92 PL_FPrintError(err, "Cannot acquire status of file"); |
|
93 rv = 1; goto done; |
|
94 } |
|
95 if (seek > 0) |
|
96 { |
|
97 if (seek > fileInfo.size) seek = 0; |
|
98 position = PR_Seek(file, (fileInfo.size - seek), PR_SEEK_SET); |
|
99 if (-1 == (PRInt32)position) |
|
100 PL_FPrintError(err, "Cannot seek to starting position"); |
|
101 } |
|
102 |
|
103 do |
|
104 { |
|
105 while (position < fileInfo.size) |
|
106 { |
|
107 PRInt32 read, bytes = fileInfo.size - position; |
|
108 if (bytes > sizeof(buffer)) bytes = sizeof(buffer); |
|
109 read = PR_Read(file, buffer, bytes); |
|
110 if (read != bytes) |
|
111 PL_FPrintError(err, "Cannot read to eof"); |
|
112 position += read; |
|
113 PR_Write(out, buffer, read); |
|
114 } |
|
115 |
|
116 if (follow) |
|
117 { |
|
118 PR_Sleep(dally); |
|
119 status = PR_GetOpenFileInfo(file, &fileInfo); |
|
120 if (PR_FAILURE == status) |
|
121 { |
|
122 PL_FPrintError(err, "Cannot acquire status of file"); |
|
123 rv = 1; goto done; |
|
124 } |
|
125 } |
|
126 } while (follow); |
|
127 |
|
128 done: |
|
129 PR_Close(file); |
|
130 |
|
131 return rv; |
|
132 } /* main */ |
|
133 |
|
134 /* tail.c */ |