]> git.pond.sub.org Git - empserver/blob - src/lib/commands/info.c
Update copyright notice
[empserver] / src / lib / commands / info.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2020, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  info.c: display an info page
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1986
31  *     Mike Wise, 1997 - added apropos and case insensitivity
32  *     Doug Hay, 1998
33  *     Steve McClure, 1998-2000
34  *     Ron Koenderink, 2004
35  */
36
37 #include <config.h>
38
39 #include <ctype.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42 #include <stdio.h>
43 #if !defined(_WIN32)
44 #include <dirent.h>
45 #endif
46 #include "commands.h"
47 #include "optlist.h"
48
49 static char *
50 lowerit(char *buf, int n, char *orig)
51 {                               /* converts a string to lower case */
52     /* lower case output buffer */
53     /* size of output buffer */
54     /* input strig */
55     int i;
56     for (i = 0; i < n - 1 && orig[i]; i++)
57         buf[i] = tolower(orig[i]);
58     buf[i] = 0;
59     return buf;
60 }
61
62 #if !defined(_WIN32)
63
64 int
65 info(void)
66 {
67     char buf[255];
68     FILE *fp;
69     char *name;
70     char *tmp_name;
71     struct stat statb;
72     struct dirent *dp;
73     char filename[1024];
74     char last[256];
75     DIR *info_dp;
76     int nmatch = 0;
77     int width = 0;
78     char sep;
79
80     name = player->argp[1];
81     if (name) {
82         /*
83          * don't let sneaky people go outside the info directory
84          */
85         if (NULL != (tmp_name = strrchr(name, '/')))
86             name = tmp_name + 1;
87     }
88     if (!name || !*name)
89         name = "TOP";
90
91     snprintf(filename, sizeof(filename), "%s/%s", infodir, name);
92     fp = fopen(filename, "r");
93     if (fp == NULL) {
94         /* may be a "partial" request. */
95         info_dp = opendir(infodir);
96         if (!info_dp) {
97             pr("Can't open info dir\n");
98             logerror("Can't open info dir \"%s\"\n", infodir);
99             return RET_FAIL;
100         }
101
102         while ((dp = readdir(info_dp))) {
103             if (strncasecmp(name, dp->d_name, strlen(name)) != 0)
104                 continue;
105             nmatch++;
106             if (nmatch == 1) {
107                 snprintf(last, sizeof(last), "%s", dp->d_name);
108             } else {
109                 if (nmatch == 2) {
110                     pr("`%s' is ambiguous.  The following topics match:\n%s",
111                         name, last);
112                     width = strlen(last);
113                 }
114                 width += 2 + strlen(dp->d_name);
115                 sep = ' ';
116                 if (width > 75) {
117                     sep = '\n';
118                     width = strlen(dp->d_name);
119                 }
120                 pr(",%c%s", sep, dp->d_name);
121             }
122         }
123         closedir(info_dp);
124         if (nmatch == 0) {
125             pr("Sorry, there is no info on %s\n", name);
126             return RET_FAIL;
127         } else if (nmatch > 1) {
128             pr(".\n");
129             return RET_FAIL;
130         }
131         snprintf(filename, sizeof(filename), "%s/%s", infodir, last);
132         fp = fopen(filename, "r");
133         if (fp == NULL) {
134             pr("Error reading info file for %s\n", name);
135             logerror("Cannot open for \"%s\" info file (%s)",
136                      filename, strerror(errno));
137             return RET_FAIL;
138         }
139     }
140     if (fstat(fileno(fp), &statb) < 0) {
141         pr("Error reading info file for %s\n", name);
142         logerror("Cannot fstat for \"%s\" info file (%s)",
143                  filename, strerror(errno));
144         fclose(fp);
145         return RET_FAIL;
146     }
147     if ((statb.st_mode & S_IFREG) == 0) {
148         pr("Error reading info file for %s\n", name);
149         logerror("The info file \"%s\" is not regular file\n", filename);
150         fclose(fp);
151         return RET_FAIL;
152     }
153
154     while (fgets(buf, sizeof(buf), fp))
155         pr("%s", buf);
156     (void)fclose(fp);
157     return RET_OK;
158 }
159
160 int
161 apro(void)
162 {
163     FILE *fp;
164     char *name, *lbp;
165     char *fbuf;
166     char *lbuf;
167     struct dirent *dp;
168     char filename[1024];
169     DIR *info_dp;
170     long nf, nhf, nl, nlhl, nhl, nll;
171     int alreadyhit;
172     int lhitlim;
173     struct stat statb;
174
175     if (!player->argp[1] || !*player->argp[1]) {
176         pr("Apropos what?\n");
177         return RET_FAIL;
178     }
179
180     lhitlim = 100;
181     if (player->argp[2]) {
182         lhitlim = atoi(player->argp[2]);
183         if (lhitlim <= 0)
184             lhitlim = 100;
185     }
186
187     info_dp = opendir(infodir);
188     if (info_dp == NULL) {
189         pr("Can't open info dir\n");
190         logerror("Can't open info dir \"%s\"", infodir);
191         return RET_FAIL;
192     }
193
194     fbuf = malloc(256);
195     lbuf = malloc(256);
196     lbp = malloc(256);
197
198     /*
199      *  lower case search string into lbp
200      */
201     name = player->argp[1];
202     lowerit(lbp, 256, name);
203
204     /*
205      *  search
206      */
207     nf = nhf = nl = nhl = 0;
208     while ((dp = readdir(info_dp))) {
209         if (dp->d_name[0] == '.')
210             continue;
211         snprintf(filename, sizeof(filename), "%s/%s", infodir, dp->d_name);
212         fp = fopen(filename, "r");
213         alreadyhit = 0;
214         nll = nlhl = 0;
215         if (fp != NULL) {
216             if (fstat(fileno(fp), &statb) < 0) {
217                 logerror("Cannot stat for \"%s\" info file (%s)",
218                          filename, strerror(errno));
219                 fclose(fp);
220                 continue;
221             }
222             if ((statb.st_mode & S_IFREG) == 0) {
223                 logerror("The info file \"%s\" is not regular file\n",
224                          filename);
225                 fclose(fp);
226                 continue;
227             }
228             while (fgets(fbuf, 256, fp)) {
229                 lowerit(lbuf, 256, fbuf);
230                 if (strstr(lbuf, lbp)) {
231                     if (!alreadyhit) {
232                         pr("*** %s ***\n", dp->d_name);
233                         alreadyhit = 1;
234                         nhf++;
235                     }
236                     fbuf[74] = '\n';
237                     fbuf[75] = 0;
238                     pr("   %s", fbuf);
239                     nlhl++;
240                     /*
241                      * break if too many lines
242                      */
243                     if ((nhl + nlhl) > lhitlim)
244                         break;
245                 }
246                 nll++;
247             }
248             fclose(fp);
249         }
250         nhl += nlhl;
251         nl += nll;
252         nf++;
253         if (nhl > lhitlim)
254             break;
255     }
256     closedir(info_dp);
257
258     free(fbuf);
259     free(lbuf);
260     free(lbp);
261
262     if ((nhl) > lhitlim) {
263         pr("Limit of %d lines exceeded\n", lhitlim);
264     }
265     pr("Found %s in %ld of %ld files and in %ld of %ld lines\n",
266        name, nhf, nf, nhl, nl);
267     return RET_OK;
268 }
269
270 #else  /* _WIN32 */
271
272 int
273 info(void)
274 {
275     char buf[255];
276     FILE *fp;
277     char *name;
278     char *tmp_name;
279     char filename[1024];
280     char last[256];
281     int nmatch = 0;
282     int width = 0;
283     char sep;
284
285     name = player->argp[1];
286     if (name) {
287         /*
288          * don't let sneaky people go outside the info directory
289          */
290         if (NULL != (tmp_name = strrchr(name, '/')))
291             name = tmp_name + 1;
292         if (NULL != (tmp_name = strrchr(name, '\\')))
293             name = tmp_name + 1;
294         if (NULL != (tmp_name = strrchr(name, ':')))
295             name = tmp_name + 1;
296     }
297     if (!name || !*name)
298         name = "TOP";
299
300     snprintf(filename, sizeof(filename) - 1, "%s\\%s", infodir, name);
301     fp = fopen(filename, "rb");
302     if (fp == NULL) {
303         /* may be a "partial" request. */
304         HANDLE hDir;
305         WIN32_FIND_DATA fData;
306         strcat(filename, "*");
307         hDir = FindFirstFile(filename, &fData);
308         if (hDir == INVALID_HANDLE_VALUE) {
309             switch (GetLastError()) {
310             case ERROR_FILE_NOT_FOUND:
311                 pr("Sorry, there is no info on %s\n", name);
312                 break;
313             case ERROR_PATH_NOT_FOUND:
314                 pr("Can't open info dir\n");
315                 logerror("Can't open info dir \"%s\"", infodir);
316                 break;
317             default:
318                 pr("Error reading info dir\n");
319                 logerror("Error (%lu) reading info dir(%s)\\file(%s)",
320                          GetLastError(), infodir, filename);
321             }
322             return RET_FAIL;
323         }
324         do {
325             if ((fData.dwFileAttributes != (DWORD)-1) &&
326                 ((fData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) ||
327                  (fData.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE) ||
328                  (fData.dwFileAttributes == FILE_ATTRIBUTE_READONLY)) &&
329                 (strncasecmp(name, fData.cFileName, strlen(name)) == 0)) {
330                 nmatch++;
331                 if (nmatch == 1) {
332                     snprintf(last, sizeof(last), "%s", fData.cFileName);
333                 } else {
334                     if (nmatch == 2) {
335                         pr("`%s' is ambiguous.  The following topics match:\n%s",
336                             name, last);
337                         width = strlen(last);
338                     }
339                     width += 2 + strlen(fData.cFileName);
340                     sep = ' ';
341                     if (width > 75) {
342                         sep = '\n';
343                         width = strlen(fData.cFileName);
344                     }
345                     pr(",%c%s", sep, fData.cFileName);
346                 }
347             }
348         } while (FindNextFile(hDir, &fData));
349         FindClose(hDir);
350         if (nmatch == 0) {
351             pr("Sorry, there is no info on %s\n", name);
352             return RET_FAIL;
353         } else if (nmatch > 1) {
354             pr(".\n");
355             return RET_FAIL;
356         }
357         snprintf(filename, sizeof(filename), "%s/%s", infodir, last);
358         fp = fopen(filename, "rb");
359         if (fp == NULL) {
360             pr("Error reading info file for %s\n", name);
361             logerror("Cannot open for \"%s\" info file (%s)",
362                      filename, strerror(errno));
363             return RET_FAIL;
364         }
365     } else {
366         DWORD fAttrib = GetFileAttributes(filename);
367         if ((fAttrib == (DWORD)-1) || /* INVALID_FILE_ATTRIBUTES */
368             ((fAttrib != FILE_ATTRIBUTE_NORMAL) &&
369              (fAttrib != FILE_ATTRIBUTE_ARCHIVE) &&
370              (fAttrib != FILE_ATTRIBUTE_READONLY))) {
371             pr("Error reading info file for %s\n", name);
372             logerror("The info file \"%s\" is not regular file\n",
373                      filename);
374             fclose(fp);
375             return RET_FAIL;
376         }
377     }
378
379     while (fgets(buf, sizeof(buf), fp) != 0)
380         pr("%s", buf);
381     (void)fclose(fp);
382     return RET_OK;
383 }
384
385 int
386 apro(void)
387 {
388     HANDLE hDir;
389     WIN32_FIND_DATA fData;
390     FILE *fp;
391     char *name, *lbp;
392     char *fbuf;
393     char *lbuf;
394     char filename[1024];
395     long nf, nhf, nl, nlhl, nhl, nll;
396     int alreadyhit;
397     int lhitlim;
398
399     if (player->argp[1] == 0 || !*player->argp[1]) {
400         pr("Apropos what?\n");
401         return RET_FAIL;
402     }
403
404     lhitlim = 100;
405     if (player->argp[2]) {
406         lhitlim = atoi(player->argp[2]);
407         if (lhitlim <= 0)
408             lhitlim = 100;
409     }
410
411     snprintf(filename, sizeof(filename), "%s\\*", infodir);
412     hDir = FindFirstFile(filename, &fData);
413     if (hDir == INVALID_HANDLE_VALUE) {
414         if (GetLastError() == ERROR_PATH_NOT_FOUND) {
415             pr("Can't open info dir\n");
416             logerror("Can't open info dir \"%s\"", infodir);
417         } else {
418             pr("Error reading info dir\n");
419             logerror("Error (%lu) reading info dir(%s)\\file(%s)",
420                      GetLastError(), infodir, filename);
421         }
422         return RET_FAIL;
423     }
424
425     fbuf = malloc(256);
426     lbuf = malloc(256);
427     lbp = malloc(256);
428
429     /*
430      *  lower case search string into lbp
431      */
432     name = player->argp[1];
433     lowerit(lbp, 256, name);
434
435     /*
436      *  search
437      */
438     nf = nhf = nl = nhl = 0;
439     do {
440         if ((fData.dwFileAttributes != (DWORD)-1) &&
441             ((fData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) ||
442              (fData.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE) ||
443              (fData.dwFileAttributes == FILE_ATTRIBUTE_READONLY))) {
444             snprintf(filename, sizeof(filename), "%s\\%s", infodir,
445                      fData.cFileName);
446             fp = fopen(filename, "rb");
447             alreadyhit = 0;
448             nll = nlhl = 0;
449             if (fp != NULL) {
450                 while (fgets(fbuf, 256, fp)) {
451                     lowerit(lbuf, 256, fbuf);
452                     if (strstr(lbuf, lbp)) {
453                         if (!alreadyhit) {
454                             pr("*** %s ***\n", fData.cFileName);
455                             alreadyhit = 1;
456                             nhf++;
457                         }
458                         fbuf[74] = '\n';
459                         fbuf[75] = 0;
460                         pr("   %s", fbuf);
461                         nlhl++;
462                         /*
463                          * break if too many lines
464                          */
465                         if ((nhl + nlhl) > lhitlim)
466                             break;
467                     }
468                     nll++;
469                 }
470                 fclose(fp);
471             }
472             nhl += nlhl;
473             nl += nll;
474             nf++;
475             if (nhl > lhitlim)
476                 break;
477         }
478     } while (FindNextFile(hDir, &fData));
479     FindClose(hDir);
480
481     free(fbuf);
482     free(lbuf);
483     free(lbp);
484
485     if ((nhl) > lhitlim) {
486         pr("Limit of %d lines exceeded\n", lhitlim);
487     }
488     pr("Found %s in %ld of %ld files and in %ld of %ld lines\n",
489        name, nhf, nf, nhl, nl);
490     return RET_OK;
491 }
492
493 #endif  /* _WIN32 */