]> git.pond.sub.org Git - empserver/blob - src/lib/commands/info.c
(index, rindex): Obsolete BSDisms; remove. Use standard strchr() and
[empserver] / src / lib / commands / info.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program 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 2 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, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  info.c: display an info page
29  * 
30  *  Known contributors to this file:
31  *     Dave Pare, 1986
32  *     Mike Wise, 1997 - added apropos and case insensitivity
33  *     Doug Hay, 1998
34  *     Steve McClure, 1998-2000
35  */
36
37 #include "misc.h"
38 #include "player.h"
39 #include <errno.h>
40 #include <string.h>
41 #include <stdio.h>
42 #include <sys/stat.h>
43 #if !defined(_WIN32)
44 #include <dirent.h>
45 #else
46 #include <windows.h>
47 #endif
48 #ifdef solaris
49 #include <unistd.h>
50 #endif
51 #include "commands.h"
52
53 #if 0
54 static int fileselect(struct dirent *dp);
55 static int printdir(void);
56 #endif
57
58 static
59 s_char *
60 lowerit(s_char *buf, int n, s_char *orig)
61 {                               /* converts a string to lower case */
62     /* lower case output buffer */
63     /* size of output buffer */
64     /* input strig */
65     int i;
66     s_char *tmp;
67     tmp = buf;
68     memset(buf, 0, n);
69     for (i = 0; i < n && *orig; i++) {
70         *tmp++ = tolower(*orig++);
71     }
72     return buf;
73 }
74
75 static
76     int
77 strnccmp(s_char *s1, s_char *s2, int n)
78 {
79     int i;
80     char c1, c2;
81     for (i = 0; i < n && *s1 && s2; i++) {
82         c1 = tolower(*s1++);
83         c2 = tolower(*s2++);
84         if (c1 > c2)
85             return 1;
86         else if (c1 < c2)
87             return -1;
88     }
89     return 0;
90 }
91
92 #if !defined(_WIN32)
93
94 int
95 info(void)
96 {
97     extern s_char *infodir;
98     s_char buf[255];
99     FILE *fp;
100     s_char *bp;
101     struct stat statb;
102     struct dirent *dp;
103     s_char filename[1024];
104     DIR *info_dp;
105
106     if (player->argp[1] == 0 || !*player->argp[1])
107         bp = "TOP";
108 #if 0
109     /* Note that we generate an "all" file now, which contains
110      * this list, so we don't do printdir. */
111     else if (!strcmp(player->argp[1], "all"))
112         return printdir();
113 #endif
114     /*
115      * don't let sneaky people go outside the info directory
116      */
117     else if (NULL != (bp = strrchr(player->argp[1], '/')))
118         bp++;
119     else
120         bp = player->argp[1];
121     sprintf(filename, "%s/%s", infodir, bp);
122     fp = fopen(filename, "r");
123     if (fp == NULL) {
124         int len = strlen(bp);
125         /* may be a "partial" request.  */
126         info_dp = opendir(infodir);
127         if (info_dp == 0) {
128             pr("Can't open info dir \"%s\"\n", infodir);
129             return RET_SYS;
130         }
131         rewinddir(info_dp);
132         while ((dp = readdir(info_dp)) != 0 && fp == 0) {
133             if (strnccmp(bp, dp->d_name, len) != 0)
134                 continue;
135             sprintf(filename, "%s/%s", infodir, dp->d_name);
136             fp = fopen(filename, "r");
137         }
138         closedir(info_dp);
139         if (fp == NULL) {
140             pr("Sorry, there is no info on %s\n", bp);
141             return RET_FAIL;
142         }
143     }
144     if (fstat(fileno(fp), &statb) < 0) {
145         pr("Cannot read info page for \"%s\" (%s)\n",
146            dp->d_name, strerror(errno));
147         fclose(fp);
148         return RET_SYS;
149     }
150     if ((statb.st_mode & S_IFREG) == 0) {
151         pr("There is no available information on \"%s\"\n", dp->d_name);
152         fclose(fp);
153         return RET_FAIL;
154     }
155     pr("Information on:  %s    Last modification date: %s",
156        bp, ctime(&statb.st_mtime));
157     while (fgets(buf, sizeof(buf), fp) != 0)
158         pr("%s", buf);
159     (void)fclose(fp);
160     return RET_OK;
161 }
162
163 #if 0
164 static int biggest;
165
166 static
167     int
168 fileselect(struct dirent *dp)
169 {
170     int l;
171     if (*dp->d_name == '.')
172         return 0;
173
174     if ((l = strlen(dp->d_name)) > biggest)
175         biggest = l;
176     return 1;
177 }
178
179 #ifdef solaris
180 static
181     int
182 alphasort(struct dirent *d1, struct dirent *d2)
183 {
184     return strcmp(d1->d_name, d2->d_name);
185 }
186
187 static int
188 scandir(char *dir, struct dirent ***dpp, int (*select) (), int (*sort) ())
189      /* directory to read */
190      /* directory entry pointer pointer */
191 {
192     DIR *dirp;
193     int nsize;
194     int nents;
195     struct dirent *d, *tmp;
196
197     d = (struct dirent *)malloc(sizeof(struct dirent) +
198                                 pathconf(".", _PC_NAME_MAX) + 1);
199     if ((dirp = opendir(dir)) == NULL)
200         return -1;
201     nsize = 100;
202     *dpp = (struct dirent **)malloc(nsize * sizeof(struct dirent *));
203     if ((*dpp) == NULL) {
204         closedir(dirp);
205         return -1;
206     }
207     nents = 0;
208     while ((d = readdir_r(dirp, d)) != NULL) {
209         if (select != NULL && !(*select) (d))
210             continue;
211         tmp =
212             (struct dirent *)malloc(sizeof(struct dirent) +
213                                     strlen(d->d_name) + 1);
214         if (tmp == NULL)
215             return -1;
216         tmp->d_ino = d->d_ino;
217         tmp->d_reclen = d->d_reclen;
218         strcpy(tmp->d_name, d->d_name);
219         if (nents > nsize) {
220             *dpp =
221                 (struct dirent **)realloc((char *)*dpp,
222                                           nsize * sizeof(struct dirent *));
223             if (*dpp == NULL)
224                 return -1;
225         }
226         (*dpp)[nents++] = tmp;
227     }
228     closedir(dirp);
229     if (nents > 0 && sort != NULL)
230         qsort(*dpp, nents, sizeof(struct dirent *), sort);
231     return nents;
232 }
233 #endif
234
235 static int
236 printdir(void)
237 {
238 #if !defined(solaris) && !defined(ALPHA) && !defined(__linux__)
239     extern int alphasort(const struct dirent *const *,
240                          const struct dirent *const *);
241 #endif
242     extern s_char *infodir;
243     static time_t lastmodtime;
244     static int number;
245     static struct dirent **dp;
246     struct stat st;
247     int count;
248     int i;
249     int npl;
250
251     if (stat(infodir, &st) == -1)
252         return RET_FAIL;
253     pr("Available topics are:\n");
254     if (lastmodtime < st.st_mtime) {
255         if (dp)
256             free((s_char *)dp);
257         biggest = 0;
258         number = scandir(infodir, &dp, fileselect, alphasort);
259         lastmodtime = st.st_mtime;
260     }
261     count = 79 / (biggest + 1) - 1;
262     for (i = 0, npl = 0; i < number; i++) {
263         pr("%-*.*s%c", biggest, strlen(dp[i]->d_name),
264            dp[i]->d_name, npl == count ? '\n' : ' ');
265         if (npl == count)
266             npl = 0;
267         else
268             npl++;
269     }
270     if (npl != 0)
271         pr("\n");
272     return 0;
273 }
274 #endif
275
276 int
277 apro(void)
278 {
279     extern s_char *infodir;
280     FILE *fp;
281     s_char *bp, *lbp;
282     s_char *fbuf;
283     s_char *lbuf;
284     struct dirent *dp;
285     s_char filename[1024];
286     DIR *info_dp;
287     long nf, nhf, nl, nlhl, nhl, nll;
288     int alreadyhit;
289     int lhitlim;
290
291     if (player->argp[1] == 0 || !*player->argp[1]) {
292         pr("Apropos what?\n");
293         return RET_FAIL;
294     }
295
296     lhitlim = 100;
297     if (player->argp[2]) {
298         lhitlim = atoi(player->argp[2]);
299         if (lhitlim <= 0)
300             lhitlim = 100;
301     }
302
303     info_dp = opendir(infodir);
304     if (info_dp == 0) {
305         pr("Can't open info dir \"%s\"\n", infodir);
306         return RET_SYS;
307     }
308
309     fbuf = (s_char *)malloc(256);
310     lbuf = (s_char *)malloc(256);
311     lbp = (s_char *)malloc(256);
312
313     /*
314      *  lower case search string into lbp
315      */
316     bp = player->argp[1];
317     lowerit(lbp, 256, bp);
318
319     /*
320      *  search
321      */
322     nf = nhf = nl = nhl = 0;
323     rewinddir(info_dp);
324     while ((dp = readdir(info_dp)) != 0) {
325         sprintf(filename, "%s/%s", infodir, dp->d_name);
326         fp = fopen(filename, "r");
327         alreadyhit = 0;
328         nll = nlhl = 0;
329         if (fp != NULL) {
330             while (fgets(fbuf, 256, fp)) {
331                 lowerit(lbuf, 256, fbuf);
332                 if (strstr(lbuf, lbp)) {
333                     if (!alreadyhit) {
334                         pr("*** %s ***\n", dp->d_name);
335                         alreadyhit = 1;
336                         nhf++;
337                     }
338                     fbuf[74] = '\n';
339                     fbuf[75] = 0;
340                     pr("   %s", fbuf);
341                     nlhl++;
342                     /*
343                      * break if too many lines
344                      */
345                     if ((nhl + nlhl) > lhitlim)
346                         break;
347                 }
348                 nll++;
349             }
350             fclose(fp);
351         }
352         nhl += nlhl;
353         nl += nll;
354         nf++;
355         if (nhl > lhitlim)
356             break;
357     }
358     closedir(info_dp);
359
360     free(fbuf);
361     free(lbuf);
362     free(lbp);
363
364     if ((nhl) > lhitlim) {
365         pr("Limit of %ld lines exceeded\n", lhitlim);
366     }
367     pr("Found %s in %ld of %ld files and in %ld of %ld lines\n",
368        bp, nhf, nf, nhl, nl);
369     return RET_OK;
370 }
371
372 #else
373
374 int
375 info(void)
376 {
377     extern s_char *infodir;
378     s_char buf[255];
379     FILE *fp;
380     s_char *bp;
381     s_char *bp2;
382     s_char filename[1024];
383
384     if (player->argp[1] == 0 || !*player->argp[1])
385         bp = "TOP";
386 #if 0
387     /* Note that we generate an "all" file now, which contains
388      * this list, so we don't do printdir. */
389     else if (!strcmp(player->argp[1], "all"))
390         return printdir();
391 #endif
392     else {
393         /*
394          * don't let sneaky people go outside the info directory
395          */
396         bp = player->argp[1];
397         if (NULL != (bp2 = strrchr(bp, '/')))
398             bp = ++bp2;
399         if (NULL != (bp2 = strrchr(bp, '\\')))
400             bp = ++bp2;
401         if (NULL != (bp2 = strrchr(bp, ':')))
402             bp = ++bp2;
403         if (!*bp)
404             bp = "TOP";
405     }
406
407     strncpy(filename, infodir, sizeof(filename) - 2);
408     strcat(filename, "//");
409     strncat(filename, bp, sizeof(filename) - 1 - strlen(filename));
410     fp = fopen(filename, "r");
411     if (fp == NULL) {
412         /* may be a "partial" request.  */
413         HANDLE hDir;
414         WIN32_FIND_DATA fData;
415         int len = strlen(bp);
416         strncat(filename, "*", sizeof(filename) - 1 - strlen(filename));
417         hDir = FindFirstFile(filename, &fData);
418         if (hDir == INVALID_HANDLE_VALUE) {
419             pr("Can't open info dir \"%s\"\n", infodir);
420             return RET_SYS;
421         }
422         do {
423             if ((fData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) &&
424                 (strnccmp(bp, fData.cFileName, len) == 0)) {
425                 strncpy(filename, infodir, sizeof(filename) - 2);
426                 strcat(filename, "//");
427                 strncat(filename, fData.cFileName,
428                         sizeof(filename) - 1 - strlen(filename));
429                 fp = fopen(filename, "r");
430             }
431         } while (!fp && FindNextFile(hDir, &fData));
432         FindClose(hDir);
433         if (fp == NULL) {
434             pr("Sorry, there is no info on %s\n", bp);
435             return RET_FAIL;
436         }
437     }
438     pr("Information on:  %s", bp);
439     while (fgets(buf, sizeof(buf), fp) != 0)
440         pr("%s", buf);
441     (void)fclose(fp);
442     return RET_OK;
443 }
444
445 #if 0
446 static int
447 printdir(void)
448 {
449     extern s_char *infodir;
450     HANDLE hDir;
451     WIN32_FIND_DATA fData;
452     int count;
453     int i;
454     int npl;
455     s_char filename[1024];
456
457     strncpy(filename, infodir, sizeof(filename) - 3);
458     strcat(filename, "//*");
459
460     hDir = FindFirstFile(filename, &fData);
461     if (hDir == INVALID_HANDLE_VALUE) {
462         return RET_FAIL;
463     }
464
465     pr("Available topics are:\n");
466     do {
467         if (fData.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) {
468             /* Yes, we could do multi-column work here. */
469             pr("%s\n", fData.cFileName);
470         }
471     } while (FindNextFile(hDir, &fData));
472     FindClose(hDir);
473
474     return 0;
475 }
476 #endif
477
478 int
479 apro(void)
480 {
481     extern s_char *infodir;
482     HANDLE hDir;
483     WIN32_FIND_DATA fData;
484     FILE *fp;
485     s_char *bp, *lbp;
486     s_char *fbuf;
487     s_char *lbuf;
488     s_char filename[1024];
489     long nf, nhf, nl, nlhl, nhl, nll;
490     int alreadyhit;
491     int lhitlim;
492
493     if (player->argp[1] == 0 || !*player->argp[1]) {
494         pr("Apropos what?\n");
495         return RET_FAIL;
496     }
497
498     lhitlim = 100;
499     if (player->argp[2]) {
500         lhitlim = atoi(player->argp[2]);
501         if (lhitlim <= 0)
502             lhitlim = 100;
503     }
504
505     strncpy(filename, infodir, sizeof(filename) - 3);
506     strcat(filename, "//*");
507     hDir = FindFirstFile(filename, &fData);
508     if (hDir == INVALID_HANDLE_VALUE) {
509         return RET_FAIL;
510     }
511
512     fbuf = (s_char *)malloc(256);
513     lbuf = (s_char *)malloc(256);
514     lbp = (s_char *)malloc(256);
515
516     /*
517      *  lower case search string into lbp
518      */
519     bp = player->argp[1];
520     lowerit(lbp, 256, bp);
521
522     /*
523      *  search
524      */
525     nf = nhf = nl = nhl = 0;
526     do {
527         strncpy(filename, infodir, sizeof(filename) - 3);
528         strcat(filename, "//");
529         strncat(filename, fData.cFileName,
530                 sizeof(filename) - 1 - strlen(filename));
531         fp = fopen(filename, "r");
532         alreadyhit = 0;
533         nll = nlhl = 0;
534         if (fp != NULL) {
535             while (fgets(fbuf, 256, fp)) {
536                 lowerit(lbuf, 256, fbuf);
537                 if (strstr(lbuf, lbp)) {
538                     if (!alreadyhit) {
539                         pr("*** %s ***\n", fData.cFileName);
540                         alreadyhit = 1;
541                         nhf++;
542                     }
543                     fbuf[74] = '\n';
544                     fbuf[75] = 0;
545                     pr("   %s", fbuf);
546                     nlhl++;
547                     /*
548                      * break if too many lines
549                      */
550                     if ((nhl + nlhl) > lhitlim)
551                         break;
552                 }
553                 nll++;
554             }
555             fclose(fp);
556         }
557         nhl += nlhl;
558         nl += nll;
559         nf++;
560         if (nhl > lhitlim)
561             break;
562     } while (FindNextFile(hDir, &fData));
563     FindClose(hDir);
564
565     free(fbuf);
566     free(lbuf);
567     free(lbp);
568
569     if ((nhl) > lhitlim) {
570         pr("Limit of %ld lines exceeded\n", lhitlim);
571     }
572     pr("Found %s in %ld of %ld files and in %ld of %ld lines\n",
573        bp, nhf, nf, nhl, nl);
574     return RET_OK;
575 }
576
577 #endif