1 /*
2 * readme.c
3 *
4 * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
5 * All rights reserved.
6 *
7 * $Id: readme.c,v 1.11 2015/03/14 06:11:27 marc Exp marc $
8 *
9 */
10
11 #include "headers.h"
12
13 static const char rcsid[] __attribute__ ((used)) = "$Id: readme.c,v 1.11 2015/03/14 06:11:27 marc Exp marc $";
14
15 #define VD_SIZE 32 /* good enough for the cyclic buffer */
16
17 struct visited_dirs {
18 int size;
19 int current;
20 struct {
21 long dev;
22 long ino;
23 } cyc[VD_SIZE];
24 };
25
dir_visited(struct context * ctx,struct stat * st)26 static int dir_visited(struct context *ctx, struct stat *st)
27 {
28 int i = 0;
29
30 if (!ctx->visited_dirs)
31 ctx->visited_dirs = Xcalloc(1, sizeof(struct visited_dirs));
32 else
33 for (i = 0; i < ctx->visited_dirs->size; i++)
34 if (ctx->visited_dirs->cyc[i].ino == (long) st->st_ino && ctx->visited_dirs->cyc[i].dev == (long) st->st_dev)
35 return -1;
36
37 ctx->visited_dirs->cyc[ctx->visited_dirs->current].dev = (long) st->st_dev;
38 ctx->visited_dirs->cyc[ctx->visited_dirs->current].ino = (long) st->st_ino;
39
40 if (ctx->visited_dirs->size < VD_SIZE)
41 ctx->visited_dirs->size++;
42
43 if (++ctx->visited_dirs->current == VD_SIZE)
44 ctx->visited_dirs->current = 0;
45
46 return 0;
47 }
48
file2control(struct context * ctx,char * arg,char * file)49 void file2control(struct context *ctx, char *arg, char *file)
50 {
51 Debug((DEBUG_PROC, "+ %s (%s)\n", __func__, file ? file : "(NULL)"));
52
53 if (file && ctx->multiline_banners) {
54 int i = -1;
55 struct stat st;
56 int is_banner = file == ctx->welcome || file == ctx->banner || file == ctx->goodbye;
57 char form[PATH_MAX + 1], path[PATH_MAX + 1];
58 char *l, *t, llang[10];
59
60 if (!is_banner && ctx->readme_once && (!pickystat(ctx, &st, ctx->cwd)) && dir_visited(ctx, &st)) {
61 Debug((DEBUG_PROC, "already visited\n"));
62 DebugOut(DEBUG_PROC);
63 return;
64 }
65
66 l = llang, t = lang[ctx->lang];
67 *l++ = '-';
68 while (*t)
69 *l++ = tolower((int) *t++);
70 *l = 0;
71
72 if ((sizeof(form) <= (size_t) snprintf(form, sizeof(form), "%s/%s", (is_banner || file[0] == '/')
73 ? "" : ctx->cwd, file)) || (sizeof(path) <= (size_t) snprintf(path, sizeof(path), form, llang))) {
74 DebugOut(DEBUG_PROC);
75 return;
76 }
77
78 if (is_banner) {
79 i = open(path, O_RDONLY);
80 if (i < 0 && strstr(form, "%s")) {
81 if (sizeof(path) <= (size_t) snprintf(path, sizeof(path), form, "")) {
82 DebugOut(DEBUG_PROC);
83 return;
84 }
85 i = open(path, O_RDONLY);
86 }
87 } else {
88 if (((pickystat(ctx, &st, path) || (i = open(path, O_RDONLY)) < 0)) && strstr(form, "%s")) {
89 if (sizeof(path) <= (size_t) snprintf(path, sizeof(path), form, "")) {
90 DebugOut(DEBUG_PROC);
91 return;
92 }
93 if (!pickystat(ctx, &st, path))
94 i = open(path, O_RDONLY);
95 }
96
97 if (i > -1 && ctx->readme_notify) {
98 long days_ago = (io_now.tv_sec - st.st_mtime) / 86400;
99 char tb[2 * PATH_MAX];
100 close(i);
101
102 replyf(ctx, "%s-", arg);
103 replyf(ctx, MSG_Readme_notify_1, file);
104
105 strftime(tb, sizeof(tb), MSG_Readme_notify_2, localtime(&st.st_mtime));
106 replyf(ctx, "%s-%s", arg, tb);
107
108 if (days_ago == 1)
109 reply(ctx, MSG_Readme_notify_31);
110 else
111 replyf(ctx, MSG_Readme_notify_3n, days_ago);
112
113 DebugOut(DEBUG_PROC);
114 return;
115 }
116 }
117
118 if (i > -1) {
119 char tbuf[BUFSIZE];
120 char *lineend, *linestart = NULL;
121 size_t offset = 0;
122 ssize_t inlength;
123
124 while ((inlength = read(i, tbuf + offset, sizeof(tbuf) - 1 - offset)) > 0) {
125 inlength += offset;
126 tbuf[inlength] = 0;
127 linestart = tbuf;
128 while ((lineend = strchr(linestart, '\n'))) {
129 *lineend = 0;
130 chomp(linestart);
131 replyf(ctx, "%s-%s\r\n", arg, cook(ctx, linestart, NULL, NULL, 0));
132 linestart = lineend + 1;
133 }
134 #ifdef README_LOOP
135 /*
136 * Don't allow arbitrary sized README files. Noone's able or willing to
137 * read thousands of lines rushing by. BUFSIZE is our upper limit. For
138 * unlimited file sizes, #define README_LOOP. Yes, that could be made
139 * a configuration option. No, I don't think it makes sense.
140 */
141 if ((offset = tbuf + inlength - linestart))
142 memmove(tbuf, linestart, offset);
143 else
144 #endif
145 break;
146 }
147 close(i);
148 }
149 }
150 DebugOut(DEBUG_PROC);
151 }
152