1 /*****************************************************************************/
2 /* */
3 /* (C) Copyright 1995-1997 Alberto Pasquale */
4 /* Portions (C) Copyright 1999 Per Lundberg */
5 /* */
6 /* A L L R I G H T S R E S E R V E D */
7 /* */
8 /*****************************************************************************/
9 /* */
10 /* This source code is NOT in the public domain and it CANNOT be used or */
11 /* distributed without written permission from the author. */
12 /* */
13 /*****************************************************************************/
14 /* */
15 /* How to contact the author: Alberto Pasquale of 2:332/504@fidonet */
16 /* Viale Verdi 106 */
17 /* 41100 Modena */
18 /* Italy */
19 /* */
20 /*****************************************************************************/
21
22 // FBlib.Cpp
23
24 // This is the main module of FBlib.LIB, which is a library for
25 // (selectively) building the Maximus (tm) filebase.
26
27 #include <bbsgenlb.hpp>
28 #include <apgenlib.hpp>
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <time.h>
34 #include <sys/stat.h>
35
36 #include "namelist.hpp"
37 #include "fbd.hpp"
38 #include "fb.hpp"
39 #include "fblib.hpp"
40
41
42 struct AreaData {
43 word areanum;
44 BOOL LocBase; // FALSE if the area DAT/DMP/IDX must not be written
45 char filesbbs[PATH_MAX]; // complete files.bbs name (with extension)
46 FILE *fdat, *fdmp;
47 word datpos;
48 dword dmpofs;
49 dword first; // number of first idx entry in FBD data
50 };
51
52
strnupcpy(char * dest,const char * src,int n)53 char *strnupcpy (char *dest, const char *src, int n)
54 {
55 for (int i = 0; i < n; i++) {
56 *(dest ++) = (char) toupper (*src);
57 if (*src)
58 src ++;
59 }
60 return dest;
61 }
62
63
64 // for GetEntry flags
65 #define _GE_NRM_ 0
66 #define _GE_NODIR_ 1 // do not read dir to get size and date
67
68 static char *GetEntry (const char *filename, _dir *&entry, char *&path,
69 word flags = _GE_NRM_);
70
71 // gets data on filename, to be used for files that reside in a different dir.
72 // filename can include path information.
73 // entry and path must point to suitable buffers.
74 // On return path is changed to NULL if no path in filename;
75 // entry is changed to NULL if filename is offline.
76 // The return value is the fname (no path) and points to entry->fname,
77 // even if the file is offline and entry is changed to NULL.
78 // The return value is NULL in case of error.
79
80
81
NewFBBS(const char * filepath,const char * filesbbs)82 static BOOL NewFBBS (const char *filepath, const char *filesbbs)
83 {
84 char files[PATH_MAX];
85 if (*filesbbs)
86 strcpy (files, filesbbs);
87 else
88 sprintf (files, "%sfiles.bbs", filepath);
89
90 time_t datebbs, dateidx;
91
92 datebbs = DosFileTime (files);
93 setext (files, ".IDX");
94 dateidx = DosFileTime (files);
95 if (dateidx == 0)
96 return FALSE;
97
98 return (datebbs > dateidx);
99 }
100
101
AreaNameMatch(const char * areaname)102 BOOL FileBase::AreaNameMatch (const char *areaname)
103 {
104 char *name = AreaNameList->get (1);
105 while (name) {
106 if (fnamecmp (areaname, name) == 0)
107 return TRUE;
108 name = AreaNameList->get ();
109 }
110 return FALSE;
111 }
112
113
AreaPathMatch(const char * areapath)114 BOOL FileBase::AreaPathMatch (const char *areapath)
115 {
116 char *path = AreaPathList->get (1);
117 while (path) {
118 if (eqpath (areapath, path))
119 return TRUE;
120 path = AreaPathList->get ();
121 }
122 return FALSE;
123 }
124
125
126 // -----------------> public
127
128
FileBase(char cont,int cpos,word flags)129 FileBase::FileBase (char cont, int cpos, word flags)
130 {
131 HardErrDisable ();
132
133 this->cont = cont;
134 this->cpos = cpos;
135 this->flags = flags;
136
137 AreaNameList = new namelist;
138 AreaPathList = new namelist;
139 fbd = new FBD;
140 ad = new AreaData;
141 }
142
143
~FileBase(void)144 FileBase::~FileBase (void)
145 {
146 delete ad;
147 delete fbd;
148 delete AreaPathList;
149 delete AreaNameList;
150 }
151
152
AreaOpen(word areanum,const char * filesbbs,BOOL NoLocBase)153 int FileBase::AreaOpen (word areanum, const char *filesbbs, BOOL NoLocBase)
154 {
155 char fullpath[PATH_MAX];
156
157 strcpy (fullpath, filesbbs);
158 char *p = strrchr (fullpath, DIRSEP); // remove filename
159 if (!p)
160 p = strchr (fullpath, ':');
161 if (p)
162 *(p+1) = '\0';
163 else
164 *fullpath = '\0';
165
166 int fpstat = Exist (fullpath);
167 if (!(fpstat & _ExDIR_)) // path for files.* not existent
168 return _BbsPathNotFound_;
169
170 ad->LocBase = !NoLocBase && (fpstat & _ExWRITE_); // DAT/DMP/IDX
171
172 fbd->AddArea (areanum);
173 ad->areanum = areanum;
174 ad->datpos = 0;
175
176 if (!ad->LocBase)
177 if (NoLocBase)
178 return _OK_;
179 else
180 return _ReadOnly_;
181
182 strcpy (fullpath, filesbbs);
183 setext (fullpath, ".DAT");
184
185 ad->fdat = fopen (fullpath, "wb");
186 if (ad->fdat == NULL) { // must be write protected
187 ad->LocBase = FALSE;
188 return _WriteProtected_;
189 }
190 setvbuf (ad->fdat, NULL, _IOFBF, 8192);
191
192 setext (fullpath, ".DMP");
193 ad->fdmp = fopen (fullpath, "wb");
194 if (ad->fdmp == NULL) { // this is error
195 fclose (ad->fdat);
196 ad->LocBase = FALSE;
197 return _Error_;
198 }
199 setvbuf (ad->fdmp, NULL, _IOFBF, 8192);
200
201 if (fwrite ("\0\0\0", 3, 1, ad->fdmp) != 1) {
202 fclose (ad->fdat);
203 fclose (ad->fdmp);
204 ad->LocBase = FALSE;
205 return _Error_;
206 }
207
208 strcpy (ad->filesbbs, filesbbs);
209
210 ad->dmpofs = 3;
211 ad->first = fbd->GetCurn ();
212
213 return _OK_;
214 }
215
216
WriteUniqueDmpLine(const char * text,FILE * f)217 static int WriteUniqueDmpLine (const char *text, FILE *f) // 0 on success
218 {
219 const char *p = text;
220 while (*p) {
221 int linelen = strcspn (p, "\n"); // output line
222 if (linelen > 0) {
223 if (fwrite (p, linelen, 1, f) != 1)
224 return -1;
225 p += linelen;
226 }
227 while (*p == '\n') { // change '\n' to ' '
228 if (fputc (' ', f) == EOF)
229 return -1;
230 p ++;
231 }
232 }
233
234 if (fputc ('\0', f) == EOF) // NULL termination
235 return -1;
236
237 return 0;
238 }
239
240
DmpWrite(const char * text,dword * ofs)241 int FileBase::DmpWrite (const char *text, dword *ofs)
242 {
243 *ofs = ad->dmpofs;
244 word dmpreclen = (word) (strlen (text) + 1); // null terminated !
245 if (fwrite (&dmpreclen, sizeof (dmpreclen), 1, ad->fdmp) != 1)
246 return -1;
247 if (flags & _UniqueDmpLine_) {
248 if (WriteUniqueDmpLine (text, ad->fdmp))
249 return -1;
250 } else {
251 if (fwrite (text, dmpreclen, 1, ad->fdmp) != 1)
252 return -1;
253 }
254 ad->dmpofs += (dmpreclen + sizeof (dmpreclen));
255 return 0;
256 }
257
258
AreaAddEntry(const char * filename,word flag,const _stamp * cre_date,const _stamp * mod_date,dword size,const char * desc,const char * acs,const char * path)259 int FileBase::AreaAddEntry (const char *filename, word flag,
260 const _stamp *cre_date, const _stamp *mod_date,
261 dword size, const char *desc, const char *acs,
262 const char *path)
263 {
264 if (ad->LocBase) {
265 // write .dat
266 FDAT fd;
267 memset (&fd, 0, sizeof (fd));
268
269 strnupcpy ((char *)fd.name, filename, MAX_FN_LEN);
270 fd.struct_len = sizeof (fd);
271 fd.flag = flag;
272 fd.fdate.msg_st = *mod_date; // actual filedate
273 fd.udate.msg_st = *cre_date; // upload filedate
274 fd.fsize = size;
275 // .DMP records
276 if (DmpWrite (desc, &fd.desc))
277 return -1;
278
279 if (path) {
280 if (DmpWrite (path, &fd.path))
281 return -1;
282 }
283
284 if (DmpWrite (acs, &fd.acs))
285 return -1;
286
287 if (fwrite (&fd, sizeof (fd), 1, ad->fdat) != 1) // .DAT record
288 return -1;
289 }
290
291 // store data for .idx
292
293 fbd->Store (filename, ad->areanum, ad->datpos);
294
295 ad->datpos ++;
296
297 return 0;
298 }
299
300
AreaClose(void)301 int FileBase::AreaClose (void)
302 {
303 int error = 0;
304
305 if (ad->LocBase) {
306 error |= fclose (ad->fdmp);
307 error |= fclose (ad->fdat);
308
309 // build files.idx
310
311 char fullpath[PATH_MAX];
312 strcpy (fullpath, ad->filesbbs);
313 setext (fullpath, ".IDX");
314
315 error |= fbd->Build (ad->first, fullpath);
316 }
317
318 return error;
319 }
320
321
AddName(const char * areaname,const char * AreaDat)322 void FileBase::AddName (const char *areaname, const char *AreaDat)
323 {
324 if (!AreaDat) {
325 AreaNameList->add (areaname);
326 return;
327 }
328
329 // if AreaDat -> add the Upload path !
330
331 FAREADAT fadat;
332
333 if (fadat.OpenFAreaDat (AreaDat))
334 return;
335
336 FAH *fap;
337 BOOL WildName = (strpbrk (areaname, "?*") != NULL);
338 while ((fap = fadat.NextArea (FAD_AreasOnly)) != NULL) {
339 if (fnamecmp (PFAS (fap, name), areaname) == 0) {
340 if (*PFAS (fap, uppath) != '\0')
341 AreaPathList->add (PFAS (fap, uppath));
342 if (!WildName)
343 break;
344 }
345 }
346 }
347
348
AddPath(const char * areapath)349 void FileBase::AddPath (const char *areapath)
350 {
351 AreaPathList->add (areapath);
352 }
353
354
IsBuild(const FAH * fahp,word action)355 BOOL FileBase::IsBuild (const FAH *fahp, word action)
356 {
357 if (action & _FBSkipSlow_)
358 return !(fahp->fa.attribs & FA_SLOW);
359
360 if (!(action & _FBUpdate_) && !(action & _FBMarkedOnly_))
361 return TRUE;
362
363 if (action & _FBNewAsMarked_)
364 if (!(fahp->fa.attribs & FA_NONEW))
365 if (NewFBBS (PFAS (fahp, downpath), PFAS (fahp, filesbbs)))
366 return TRUE;
367
368 if (AreaNameMatch (PFAS (fahp, name)) ||
369 AreaPathMatch (PFAS (fahp, downpath)))
370 return TRUE;
371
372 return FALSE;
373 }
374
375
GetAttribs(word attribs,word action)376 static word GetAttribs (word attribs, word action)
377 {
378 if ((attribs & (FA_AUTODATE|FA_MANDATE|FA_LISTDATE)) == 0)
379 if (action & _FBAutoDate_)
380 attribs |= FA_AUTODATE;
381 else
382 attribs |= FA_MANDATE;
383
384 return attribs;
385 }
386
387
Build(const char * AreaDat,word action,const char * FullIdx,const char * NoDupeIdx,ShowF sf,DupeF df)388 int FileBase::Build (const char *AreaDat, word action, const char *FullIdx,
389 const char *NoDupeIdx, ShowF sf, DupeF df)
390 {
391 int ret = 0;
392
393 if (AreaDat) {
394 FAREADAT fadat;
395 if (fadat.OpenFAreaDat (AreaDat) != 0)
396 return -1;
397
398 FAH *fahp; // Build marked areas
399 while ((fahp = fadat.NextArea (FAD_AreasOnly)) != NULL) {
400 if (fahp->fa.attribs & FA_NOINDEX)
401 continue;
402 if (IsBuild (fahp, action)) {
403 if (sf)
404 sf (PFAS (fahp, name), PFAS (fahp, downpath), _BegProc_);
405 word attribs = GetAttribs (fahp->fa.attribs, action);
406 int AStat = BuildArea (fadat.AreaNum (),
407 PFAS (fahp, downpath),
408 PFAS (fahp, filesbbs),
409 PFAS (fahp, acs),
410 attribs,
411 fahp->fa.date_style,
412 action & _FBNoLocBase_);
413 if (sf)
414 sf (PFAS (fahp, name), PFAS (fahp, downpath), AStat);
415 if (AStat == _Error_)
416 ret = -1;
417 }
418 }
419 }
420
421 ret |= fbd->Build ((action & _FBUpdate_) ? FBD_Update : FBD_BuildAll,
422 FullIdx, NoDupeIdx, df);
423
424 return ret;
425 }
426
427
BuildArea(word areanum,const char * filepath,const char * filesbbs,const char * acs,word fattr,sword date_style,BOOL NoLocBase)428 int FileBase::BuildArea (word areanum, const char *filepath,
429 const char *filesbbs, const char *acs,
430 word fattr, sword date_style, BOOL NoLocBase)
431 {
432 if (fattr & FA_AUTODATE)
433 if (!(Exist (filepath) & _ExDIR_)) // path not existent
434 return _FilePathNotFound_;
435
436 char filbuf[PATH_MAX];
437 const char *files;
438
439 if (*filesbbs)
440 files = filesbbs;
441 else {
442 strcpy (filbuf, filepath);
443 strcat (filbuf, "FILES.BBS");
444 files = filbuf;
445 }
446
447 if ((fattr & FA_SLOW) && (*filesbbs == '\0'))
448 NoLocBase = TRUE;
449
450 int AStat = AreaOpen (areanum, files, NoLocBase);
451
452 if (AStat == _BbsPathNotFound_)
453 return AStat;
454
455 FBBS *fbbs = new FBBS (files, PATH_MAX, DMP_BUF_SIZE, FBBSLSIZE, cont, cpos,
456 (fattr & FA_LISTDATE) ? Max2FbbsDateStyle (date_style) : FBBS_FLAG_NODATESIZE);
457
458 word flag;
459 time_t date;
460 dword size;
461 char *filename = new char[PATH_MAX];
462 char *descbuf = new char[DMP_BUF_SIZE];
463
464 int res = fbbs->GetEntry (filename, descbuf, &flag, FBBS_GE_FIRST,
465 &date, &size);
466 if (res < 0) {
467 delete fbbs;
468 delete[] descbuf;
469 delete[] filename;
470 if (AreaClose ())
471 return _Error_;
472 else
473 return _NoFilesBbs_;
474 }
475
476 DIRcl *areadir = NULL;
477
478 if (fattr & FA_AUTODATE)
479 areadir = new DIRcl (filepath); // read directory
480
481 byte *entrybuf = new byte[sizeof (_dir) + PATH_MAX - 1];
482 char *linebuf = new char[DMP_BUF_SIZE];
483 char *pathbuf = new char[PATH_MAX];
484
485 int error = 0;
486
487 while (res == 0) {
488
489 _dir *entry = (_dir *) entrybuf;
490 char *path = pathbuf;
491 char *fname; // points to filename, no path
492
493 if (fattr & FA_AUTODATE) {
494 #ifndef UNIX
495 if (strpbrk (filename, "\\:")) // with path
496 #else
497 if (strpbrk (filename, "/:")) // with path
498 #endif
499 fname = GetEntry (filename, entry, path);
500 else { // in current dir
501 entry = areadir->Get (filename); // search in dir
502 path = NULL;
503 fname = filename;
504 }
505 } else { // FA_LISTDATE or FA_MANDATE
506 fname = GetEntry (filename, entry, path, _GE_NODIR_);
507 if (fattr & FA_LISTDATE) {
508 entry->cdate = 0;
509 entry->mdate = date;
510 entry->fsize = size;
511 }
512 }
513
514 if (fname) {
515 _stamp cre_date, mod_date;
516 dword fsize;
517
518 if (entry) {
519 unix2stamp (entry->cdate ? entry->cdate : entry->mdate, &cre_date);
520 unix2stamp (entry->mdate, &mod_date);
521 fsize = entry->fsize;
522 } else { // offline
523 *((dword *)(&cre_date)) = 0L;
524 *((dword *)(&mod_date)) = 0L;
525 fsize = 0;
526 flag |= FF_OFFLINE;
527 }
528
529 error |= AreaAddEntry (fname, flag, &cre_date, &mod_date,
530 fsize, descbuf, acs, path);
531 if (error)
532 break;
533 }
534
535 res = fbbs->GetEntry (filename, descbuf, &flag, FBBS_GE_NEXT, &date, &size);
536 }
537
538 delete[] pathbuf;
539 delete[] linebuf;
540 delete[] entrybuf;
541
542 delete fbbs;
543 delete[] descbuf;
544 delete[] filename;
545
546 if (areadir)
547 delete areadir;
548
549 error |= AreaClose ();
550
551 if (error)
552 return _Error_;
553 else
554 return AStat;
555 }
556
557
GetEntry(const char * filename,_dir * & entry,char * & path,word flags)558 static char *GetEntry (const char *filename, _dir *&entry, char *&path,
559 word flags) // gets data on filename
560 {
561 #ifndef __QNXNTO__
562 char *pathend = strrchr (filename, DIRSEP);
563 if (!pathend)
564 pathend = strchr (filename, ':');
565 #else
566 char *pathend = strrchr((char *)filename, DIRSEP);
567 if (!pathend)
568 pathend = strchr((char *)filename, ':');
569 #endif // __QNXNTO__
570
571 const char *fname;
572 if (!pathend) { // no path
573 path = NULL;
574 fname = filename;
575 } else {
576 int len = pathend - filename + 1;
577 if (len >= PATH_MAX)
578 return NULL;
579 strncpy (path, filename, len);
580 path[len] = '\0';
581 fname = filename + len;
582 }
583
584 entry->namelen = (byte) strlen (fname);
585
586 #if defined (__DOS__)
587 if (entry->namelen >= PATH_MAX)
588 return NULL;
589 #endif
590
591 strcpy (entry->fname, fname);
592 char *ret = entry->fname;
593
594 if (flags & _GE_NODIR_) {
595 entry->Got = TRUE;
596 entry->fsize = 0;
597 entry->cdate = 0;
598 entry->mdate = 0;
599 } else {
600 _ExistStat statbuf;
601 if (!(Exist (filename, &statbuf) & _ExFILE_)) {
602 entry = NULL;
603 return ret;
604 }
605
606 entry->Got = TRUE;
607 entry->fsize = statbuf.size;
608 entry->cdate = statbuf.ctime;
609 entry->mdate = statbuf.mtime;
610 }
611
612 return ret;
613 }
614
615
616
617