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 /* How to contact the author: Alberto Pasquale of 2:332/504@fidonet */
11 /* Viale Verdi 106 */
12 /* 41100 Modena */
13 /* Italy */
14 /* */
15 /*****************************************************************************/
16
17 #include "bbsgenlb.hpp"
18 #include <apgenlib.hpp>
19 #include <string.h>
20 #include <fb.h>
21 #include <ctype.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24
Max2FbbsDateStyle(sword date_style)25 word Max2FbbsDateStyle (sword date_style)
26 {
27 switch (date_style) { // as in Max.Prm date_style
28 case 0:
29 return FBBS_FLAG_DATE_USA;
30 case 1:
31 return FBBS_FLAG_DATE_EURO;
32 case 2:
33 return FBBS_FLAG_DATE_JAPAN;
34 case 3:
35 return FBBS_FLAG_DATE_SCIENT;
36 }
37
38 return FBBS_FLAG_NODATESIZE;
39 }
40
41
isComment(const char * line)42 static BOOL isComment (const char *line)
43 {
44 if ((line[0] <= ' ') || (line[0] > '~')) // empty, ctrl, space, high ascii
45 return TRUE;
46 if ((line[0] == '-') && ((line[1] == ' ') || (line[1] == '\t'))) // comment
47 return TRUE;
48 return FALSE;
49 }
50
51
RdOpen()52 int FBBS::RdOpen ()
53 {
54 repeatln = FALSE;
55
56 if (f) {
57 rewind (f);
58 return 0;
59 }
60
61 if (WrClose ())
62 return -1;
63
64 f = fopen (filesbbs, "rt");
65 if (!f)
66 return -1;
67 setvbuf (f, NULL, _IOFBF, 8192);
68
69 return 0;
70 }
71
72
RdClose()73 void FBBS::RdClose ()
74 {
75 if (f) {
76 fclose (f);
77 f = NULL;
78 }
79 }
80
81
WrOpen()82 int FBBS::WrOpen ()
83 {
84 if (fileh != -1)
85 return 0;
86
87 RdClose ();
88 fileh = open (filesbbs, O_WRONLY | O_APPEND);
89 if (fileh == -1)
90 return -1;
91
92 return 0;
93 }
94
95
WrClose()96 int FBBS::WrClose ()
97 {
98 int err = 0;
99 if (fileh != -1) {
100 err |= (close (fileh) == -1);
101 fileh = -1;
102 }
103 return err;
104 }
105
106
Trunc()107 int FBBS::Trunc ()
108 {
109 RdClose ();
110 if (WrClose ())
111 return -1;
112
113 FILE *ft = fopen (filesbbs, "wt");
114 if (!ft)
115 return -1;
116 if (fclose (ft))
117 return -1;
118
119 return 0;
120 }
121
122
Fgetln()123 char *FBBS::Fgetln ()
124 {
125 if (repeatln) {
126 repeatln = FALSE;
127 return lastget;
128 }
129
130 lastget = _fgets (line, linesize, f);
131 return lastget;
132 }
133
134
135
FBBS(const char * path,int filesize,int descsize,int linesize,char cont,int cpos,word fbbsflags,SkipFile sf,void * ptr)136 FBBS::FBBS (const char *path, int filesize, int descsize, int linesize,
137 char cont, int cpos, word fbbsflags, SkipFile sf, void *ptr)
138 {
139 strcpy (filesbbs, path);
140
141 if (*filefrompath (path) == '\0')
142 strcat (filesbbs, "files.bbs");
143
144 this->filesize = filesize;
145 this->descsize = descsize;
146 this->linesize = linesize;
147 this->cont = cont;
148 this->cpos = cpos;
149 this->fbbsflags = fbbsflags;
150 this->sf = sf;
151 this->ptr = ptr;
152 t = NULL;
153
154 line = new char[linesize];
155
156 repeatln = FALSE;
157 lastget = NULL;
158 f = NULL;
159 fileh = -1;
160 }
161
162
~FBBS()163 FBBS::~FBBS ()
164 {
165 delete[] line;
166 WrClose ();
167 RdClose ();
168 if (t)
169 fclose (t);
170 }
171
172
IsInTokLst(const char * line,size_t toklen,const char * toklst)173 static bool IsInTokLst (const char *line, size_t toklen, const char *toklst)
174 {
175 WordStore ws ((char *)toklst);
176
177 char *tok = ws.GetFirst ();
178 while (tok) {
179 if (strlen (tok) == toklen)
180 if (strncasecmp (line, tok, toklen) == 0)
181 return true;
182 tok = ws.GetNext ();
183 }
184
185 return false;
186 }
187
188
GetDesc(const char * file,char * desc,word * flag,byte action,const char * repl,time_t * date,dword * size)189 int FBBS::GetDesc (const char *file, char *desc, word *flag, byte action,
190 const char *repl, time_t *date, dword *size)
191 {
192 if (flag)
193 *flag = 0;
194 if (date) // default inits
195 *date = 0;
196 if (size)
197 *size = ULONG_MAX;
198
199 FILE *tmp = NULL;
200 char tmpname[PATH_MAX];
201
202 if (!repl) // make sure the repl pointer is not NULL
203 repl = "";
204 if (access (filesbbs, 0)) { // if files.bbs does not exist
205 strcpy (tmpname, filesbbs);
206 tmp = fopen(tmpname, "wt"); // create empty file with correct umask
207 if (!tmp)
208 return -1;
209 if (fclose(tmp) == EOF)
210 return -1;
211 return -2;
212 }
213 if (RdOpen ())
214 return -1;
215
216 if ((action & FBBS_REMOVE) || (*repl)) { // tmp file must be used
217 strcpy (tmpname, filesbbs);
218 setext (tmpname, ".$$$");
219 tmp = fopen (tmpname, "wt");
220 if (!tmp)
221 return -1;
222 setvbuf (tmp, NULL, _IOFBF, 8192);
223 }
224
225 BOOL filedone = FALSE,
226 repldone = FALSE;
227 int filelen = strlen (file);
228
229 int res = 0;
230 while (Fgetln ()) {
231 if (!isComment (line)) {
232 int thislen = strcspn (line, " \t");
233 if (thislen == filelen)
234 if (strncasecmp (line, file, filelen) == 0) {
235 char *p = line+thislen;
236 p += strspn (p, " \t");
237 res |= descopy (p, desc, flag, date, size,
238 (action & FBBS_REMOVE) ? NULL : tmp);
239 if (res)
240 break;
241 filedone = TRUE;
242 continue;
243 }
244 if (*repl)
245 if (IsInTokLst (line, thislen, repl)) {
246 res |= descopy ();
247 if (res)
248 break;
249 repldone = TRUE;
250 continue;
251 }
252 }
253 if (tmp)
254 res |= (_fputs (line, tmp) == EOF);
255 if (res)
256 break;
257 }
258
259 if (tmp) {
260 res |= (fclose (tmp) == EOF);
261 tmp = NULL;
262
263 if ((res == 0) &&
264 (repldone || (filedone && (action & FBBS_REMOVE)))) {
265 RdClose ();
266 tunlink (filesbbs, 20);
267 res |= rename (tmpname, filesbbs);
268 } else
269 unlink (tmpname);
270 }
271
272 if (res)
273 return -1;
274
275 res = 0;
276 if (!filedone)
277 res |= 1;
278 if (*repl && !repldone)
279 res |= 2;
280
281 return res;
282 }
283
284
GetGenEntry(char * file,char * desc,word * flag,BOOL first,time_t * date,dword * size)285 int FBBS::GetGenEntry (char *file, char *desc, word *flag, BOOL first,
286 time_t *date, dword *size)
287 {
288 if (flag)
289 *flag = 0;
290 if (date) // default inits
291 *date = 0;
292 if (size)
293 *size = ULONG_MAX;
294
295 if (first || !f) {
296
297 if (RdOpen ())
298 return -1;
299
300 if (t) {
301 fclose (t);
302 t = NULL;
303 }
304 if (sf) { // open temporary file
305 char filetmp[PATH_MAX];
306 strcpy (filetmp, filesbbs);
307 setext (filetmp, "$$0");
308 t = fopen (filetmp, "wt");
309 if (!t)
310 return -1;
311 }
312 }
313
314 BOOL EntryRemoved = FALSE;
315
316 int res = 0;
317 BOOL found = (Fgetln () != NULL);
318 BOOL comment = FALSE;
319 if (found) {
320 comment = isComment (line);
321 if (comment) {
322 *file = '\0';
323 strzcpy (desc, line, descsize);
324 if (t)
325 res |= (_fputs (line, t) == EOF);
326 } else {
327 const char *p = line;
328 GetName (p, file, filesize); // get file name
329 if (sf)
330 EntryRemoved = sf (file, ptr);
331 res |= descopy (p, desc, flag, date, size, EntryRemoved ? NULL : t);
332 }
333 }
334
335 if (res || !found) {
336 if (t) {
337 res |= fclose (t);
338 t = NULL;
339 char filetmp[PATH_MAX];
340 strcpy (filetmp, filesbbs);
341 setext (filetmp, "$$0");
342 if (!res) { // rename new bbs on old one
343 RdClose ();
344 tunlink (filesbbs, 20);
345 res |= rename (filetmp, filesbbs);
346 } else
347 unlink (filetmp);
348 }
349 if (res)
350 return -1;
351 else
352 return 1;
353 }
354
355 if (EntryRemoved)
356 return 3;
357 if (comment)
358 return 2;
359 return 0;
360 }
361
362
GetEntry(char * file,char * desc,word * flag,BOOL first,time_t * date,dword * size)363 int FBBS::GetEntry (char *file, char *desc, word *flag, BOOL first,
364 time_t *date, dword *size)
365 {
366 int ret;
367 do {
368 ret = GetGenEntry (file, desc, flag, first, date, size);
369 first = FBBS_GE_NEXT;
370 } while (ret == 2); // skip comment lines
371 return ret;
372 }
373
374
GetFileFlags(const char * & desc)375 static word GetFileFlags (const char *&desc)
376 {
377 char flags[10];
378 word flag = FF_FILE; // get flags if available
379 while (*desc == '/') {
380 GetName (desc, flags, sizeof (flags)); // get flag string
381 char *f = flags;
382 while (*f) {
383 switch (tolower (*f)) {
384 case 't':
385 flag |= FF_NOTIME;
386 break;
387 case 'b':
388 flag |= FF_NOBYTES;
389 break;
390 }
391 f ++;
392 }
393 }
394 return flag;
395 }
396
397
Flags2Df(word fbbsflags)398 static byte Flags2Df (word fbbsflags)
399 {
400 if (fbbsflags & FBBS_FLAG_NODATESIZE)
401 return DF_DEFAULT;
402
403 switch (fbbsflags & FBBS_FLAG_DATEFORMAT) {
404 case FBBS_FLAG_DATE_USA:
405 return DF_USA;
406 case FBBS_FLAG_DATE_EURO:
407 return DF_EURO;
408 case FBBS_FLAG_DATE_JAPAN:
409 return DF_JAPAN;
410 case FBBS_FLAG_DATE_SCIENT:
411 return DF_SCIENT;
412 }
413
414 return DF_DEFAULT;
415 }
416
417
GetDateSize(const char * & desc,time_t * datep,dword * sizep,word fbbsflags)418 static void GetDateSize (const char *&desc, time_t *datep, dword *sizep, word fbbsflags)
419 {
420 if (fbbsflags & FBBS_FLAG_NODATESIZE) {
421 if (datep)
422 *datep = 0;
423 if (sizep)
424 *sizep = ULONG_MAX;
425 return;
426 }
427
428 byte date_format = Flags2Df (fbbsflags);
429
430 const int toksize = 10;
431 char tok[2][toksize];
432 const char *p = desc;
433
434 GetName (p, tok[0], toksize);
435 GetName (p, tok[1], toksize);
436
437 time_t date;
438 dword size;
439 int isize = 1; // let's suppose tok[1] contains size
440
441 date = dates2unix (tok[0], date_format);
442
443 if (date == 0) { // if first item is not date, let's try with second one
444 isize = 0;
445 date = dates2unix (tok[1], date_format);
446 }
447
448 if (date) {
449 char *endptr;
450 size = strtoul (tok[isize], &endptr, 10);
451 if ((*endptr != '\0') || (size == ULONG_MAX)) { // token is not length only
452 date = 0;
453 size = 0;
454 }
455 } else
456 size = 0;
457
458 if (date)
459 desc = p;
460
461 if (datep)
462 *datep = date;
463 if (sizep)
464 *sizep = size;
465 }
466
467
468
descopy(const char * p,char * desc,word * flag,time_t * date,dword * size,FILE * t)469 int FBBS::descopy (const char *p, char *desc, word *flag, time_t *date, dword *size,
470 FILE *t)
471 {
472 if (!p && (desc || flag || date || size))
473 return -1;
474
475 int desclen = 0;
476
477 if (p) {
478 GetDateSize (p, date, size, fbbsflags);
479 word flags = GetFileFlags (p);
480 if (flag)
481 *flag = flags;
482 if (desc)
483 desclen = descAddLine (p, desc, 0);
484 }
485
486 int res = 0;
487 if (t)
488 res |= (_fputs (line, t) == EOF);
489
490 if (cpos != -1) {
491 while (Fgetln ()) { // get continuation lines (no '\n')
492 int spacelen = strspn (line, " \t");
493 char *firstchar = line+spacelen;
494 if (cont != ' ') {
495 if ((spacelen == 0) && (cpos != 0))
496 break;
497 if (*firstchar != cont) // not a continuation line
498 break;
499 else {
500 firstchar ++; // skip cont char
501 if ((*firstchar == ' ') && !(fbbsflags & FBBS_FLAG_NOCONTSPACE))
502 firstchar ++; // skip space following cont
503 }
504 } else {
505 if ((spacelen < cpos) && *firstchar) // not a continuation line
506 break;
507 if (spacelen > cpos)
508 firstchar = line + cpos;
509 }
510 // this is a continuation (description) line
511 if (desc)
512 desclen = descAddLine (firstchar, desc, desclen);
513 if (t)
514 res |= (_fputs (line, t) == EOF);
515 }
516
517 repeatln = TRUE; // next Fgetln will repeat last result
518 }
519
520 if (res)
521 return -1;
522 return 0;
523 }
524
525
descAddLine(const char * p,char * desc,int desclen)526 int FBBS::descAddLine (const char *p, char *desc, int desclen)
527 {
528 int remsize = descsize - desclen;
529 if (remsize < 3) // space for '\n', at least one char, '\0'
530 return desclen;
531
532 if (desclen > 0) {
533 desc[desclen++] = '\n'; // add separator
534 remsize --;
535 }
536
537 char *tn = stpzcpy (desc+desclen, p, remsize);
538 return int (tn - desc);
539 }
540
541
PutGenEntry(const char * file,const char * desc,word flag,const char * befdesc,time_t date,dword size)542 int FBBS::PutGenEntry (const char *file, const char *desc, word flag,
543 const char *befdesc, time_t date, dword size)
544 {
545 if (WrOpen ())
546 return -1;
547
548 if (!(flag & FF_SAFE)) {
549 char *p = (char *)desc;
550 while (*p) { // remove trojan control chars
551 if ((*p < ' ') && (*p != '\n'))
552 *p = ' ';
553 p++;
554 }
555 }
556
557 BOOL isComment = FALSE; // is this a comment ?
558 if (!file)
559 isComment = TRUE;
560 else if (*file == '\0')
561 isComment = TRUE;
562
563 int err = 0;
564
565 if (!isComment) {
566 const char *b = desc; // start of 1st line
567 const char *e = b + strcspn (b, "\n"); // end of 1st line
568 int linelen = int (e - b); // lenght of 1st line
569
570 int befdesclen = 0;
571 if (befdesc)
572 befdesclen = strlen (befdesc);
573
574 int filelen = strlen (file);
575 err |= (write (fileh, file, filelen) == -1); // filename
576 int spacelen = __max (13 - filelen, 1);
577 err |= (write (fileh, " ", spacelen) == -1); // blanks
578
579 if (!(fbbsflags & FBBS_FLAG_NODATESIZE)) {
580 char sizes[13]; char dates[10];
581 sprintf (sizes, "%7lu ", size);
582 err |= (write (fileh, sizes, strlen (sizes)) == -1); // size
583 unix2dates (date, dates, Flags2Df (fbbsflags));
584 strcat (dates, " ");
585 err |= (write (fileh, dates, strlen (dates)) == -1); // date
586 }
587
588 int flaglen = 0; // flags
589 if (flag & (FF_NOTIME|FF_NOBYTES)) {
590 char flags[5];
591 flags[flaglen++] = '/';
592 if (flag & FF_NOTIME)
593 flags[flaglen++] = 't';
594 if (flag & FF_NOBYTES)
595 flags[flaglen++] = 'b';
596 flags[flaglen++] = ' ';
597 flags[flaglen] = '\0';
598 err |= (write (fileh, flags, flaglen) == -1);
599 }
600
601 if (befdesclen)
602 err |= (write (fileh, befdesc, befdesclen) == -1);
603 int writelen = __min (linelen, linesize - filelen - spacelen - flaglen - befdesclen - 2);
604 if (writelen)
605 err |= (write (fileh, desc, writelen) == -1);
606 err |= (write (fileh, "\n", 1) == -1);
607
608 if (cpos != -1) { // output continuation lines
609 // init start of continuation lines
610 char blank[] = " ";
611 if ((size_t)cpos > sizeof (blank) - 3) // space for cont, ' ', '\0'
612 cpos = sizeof (blank) - 3;
613 int contlen = cpos;
614
615 if (cont != ' ') {
616 blank[cpos] = cont;
617 if (fbbsflags & FBBS_FLAG_NOCONTSPACE)
618 contlen += 1;
619 else
620 contlen += 2; // include cont and space
621 }
622
623 while (*e) { // while lines available
624 b = e + 1; // begin on char following '\n'
625 e = b + strcspn (b, "\n"); // end of line
626 linelen = int (e - b); // length of line
627 err |= (write (fileh, blank, contlen) == -1); // blanks and cont
628 if (linelen)
629 err |= (write (fileh, b, __min (linelen, linesize - contlen - 2)) == -1); // desc line
630 err |= (write (fileh, "\n", 1) == -1);
631 }
632 }
633
634 } else { // isComment
635 int writelen = strlen (desc);
636 if (writelen)
637 err |= (write (fileh, desc, writelen) == -1);
638 err |= (write (fileh, "\n", 1) == -1);
639 }
640
641 return err;
642 }
643
644
SetDesc(const char * file,const char * desc,word flag,const char * befdesc,time_t date,dword size)645 int FBBS::SetDesc (const char *file, const char *desc, word flag, const char *befdesc,
646 time_t date, dword size)
647 {
648 int err = 0;
649
650 err |= PutGenEntry (file, desc, flag, befdesc, date, size);
651 err |= WrClose ();
652
653 return err;
654 }
655
656
657
658