Contributor: JONAS MAEBE

{
(BTW: it requires a 386 or up to run). It should be
(almost) bug free, since my boss has been running it for about a month by now
and all problems he has found have been fixed.
------------- BBSCAN.PAS -------------------
}
Program bbscan;

{$g+,a+,q-,r-,i-,q-,s-,n-,e-,x+,f-}

Uses crt, dos;

Const l = 20; {maxlength of areanames, limit of Squish statistics tools}
      maxareas = (65504-2) div (l+1); {around 3000}

Type areaarray = Record
                         nofareas: Word;
                         area: Array[0..maxareas] of String[l]
                   End;

Const ProgName = 'BackboneScan v1.14, Copyright (c) Gamefreak 1996';
      fs = $64;
      pop_fs = $a10f;
      Fidoexists: Boolean = true;

VAR fido, bb, newfido: TEXT;
    areas: ^areaarray;
    c1, c2: Word;
    tempstr: String;
    Asort: Array[0..maxareas] of Word;

PROCEDURE Init;
VAR iocheck: Integer;
    f: file;
BEGIN
     ClrScr;
     WRITELN(ProgName);
     WRITELN;
     Assign(f, 'backbone.in');
    {$i-}
     Reset(f);
    {$i+}
     iocheck := ioresult;
     IF iocheck <> 0 THEN
     CASE iocheck OF
          2,3: BEGIN
               WRITELN('File "backbone.in" not found. Please move this program into the right dir');
               WRITELN('and run it again.');
               WRITELN;
               HALT(iocheck)
               END
             ELSE
               BEGIN
               WRITELN('An error (',iocheck,') occurred while opening the file "fidonet.na".');                    WRITELN;
               HALT(iocheck)
               END
     END;
     IF FileSize(f) = 0 THEN
     BEGIN
          WRITELN('Size of file "backbone.in" = 0 bytes. Nothing to do.');
          WRITELN;
          HALT(1)
     END;
     close(f);
     assign(f, 'fidonet.na');
    {$i-}
     reset(f);
    {$i+}
     If ioresult <> 0 Then
        Begin
             rewrite(f);
             fidoexists := false
        End
      Else if filesize(f) = 0 Then fidoexists := false;
     close(f);
     Assign(fido, 'fidonet.na');
     reset(fido);
     Assign(bb, 'backbone.in');
     Reset(bb)
END;

PROCEDURE ReadAreaNames;
Var tempstr2: String[12+30];

Function Duplicate: Boolean;
Assembler;
        Asm
           cld
           les di, areas
           mov dx, [es:di]      {dx = nofareas}
           xor al, al
           test dx, dx
           jz @end
           add di, 2            {es:di = 1st string}
           xor cx, cx
           mov si, offset tempstr {ds:si points to tempstr}
           mov bl, [si]         {bx = length(tempstr)}
           mov bh, bl
           and bh, 11b          {bh = length(tempstr) mod 4}
           shr bl, 2            {bl = length(tempstr) div 4}
           mov ax, di           {save di in ax}
          @loop:
           mov cl, bl           {cl = length(tempstr) div 4}
           xor ch, ch
           db $66; repe cmpsw   {compare}
           jne @ok              {not equal? -> ok}
           mov cl, bh           {otherwise check remaining bytes}
           repe cmpsb
           je @equal
          @ok:
           mov si, offset tempstr {ds:si points to tempstr}
           add ax, l + 1           {let ax point to next string}
           mov di, ax           {and move it into si}
           dec dx               {decrease the number of areas}
           jnz @loop            {if not zero -> loop}
           xor al, al           {no equal string -> false}
           jmp @end
          @equal:
           mov al, 1            {equal -> true}
          @end:
END;

BEGIN
     WRITELN('Reading areanames from "Backbone.in" and removing duplicates...');
     WRITELN;
     IF maxavail < 65535 THEN
        BEGIN
             WRITELN('Not enough memory available.');
             WRITELN;
             close(bb);
             close(fido);
             HALT(8)
        END
         ELSE new(areas);
     fillchar(areas^, sizeof(areas^), 0);
     While (areas^.nofareas < maxareas) and not(eof(bb)) Do
           BEGIN
                Readln(bb, tempstr);
                ASM
                   cld                       {this part copies the areaname}
                   push ds                   {to the front of the string}
                   mov di, offset tempstr    {and removes the "xxx messages}
                   mov dx, di                {scanned/tossed" part.}
                   mov si, di
                   add si, 12
                   pop es                    {es:di = sortstr[0]}
                   xor cx, cx
                   mov al, ' '               {used to check length of areaname}
                   mov cl, byte[di]          {cl = length total string}
                   add di, 12                {es:di = sortstr[12]}
                   sub cl, 12
                   mov bx, cx                {save original length - 12}
                   dec bx
                   repne scasb                {scan until a space is encouterd-> eof areaname}
                   sub bx, cx                {calc length of areaname}
                   mov cx, bx                {move length(areaname in cx)}
                   mov di, dx
                   mov [di], cl              {move length of areaname in lengthbyte}
                   inc di                    {points to first char of string}
                   shr cx, 1
                   jnc @even
                   movsb
                  @even:
                   rep movsw                 {move the areaname to the front}
                END;
                If not(duplicate) Then
                   With areas^ Do
                        BEGIN
                             area[nofareas] := tempstr;
                             inc(nofareas)
                        END
           END;
           Dec(areas^.nofareas);
           close(bb)
END;

Procedure Sort;
Var areasofs: Word;
Begin
     Writeln('Sorting areanames...');
     Writeln;
     Asm
        push ds
        push ds
        dw pop_fs
        cld
        les di, areas
        mov dx, word[es:di]
        mov bx, dx
        add bx, bx
        add bx, offset asort
       @asortinit:
        mov [bx], dx
        sub bx, 2
        dec dx
        jnz @asortinit
        mov dx, [es:di]
        dec dx
        jl @end
        mov ax, dx        {ax = pred(areas^.nofareas)}
        xor dx, dx       
        lds si, areas
        add si, 3
        mov areasofs, si
        xor bx, bx        {bx = c2}
       @outloop:
        mov di, areasofs
        db fs; mov cx, [bx+offset asort+2]
        add di, cx
        shl cx, 2
        add di, cx
        shl cx, 2
        add di, cx
       @loop:
        mov si, areasofs
        db fs; mov cx, [bx+offset asort]
        add si, cx
        shl cx, 2
        add si, cx
        shl cx, 2
        add si, cx
        xor cx, cx
        mov cl, [si-1]
        cmp cl, [di-1]
        jbe @length_ok
        mov cl, [di-1]
       @length_ok:        {cl = length of shortest string}
        push si
        push di
        rep cmpsb         {compare the strings}
        pop si            {si = pushed di and di = pushed si, used so I}
        pop di            {have to recalculate di in the next loop}
        jb @noswitch      {if first < second, don't switch}
        ja @switch        {if first > second, switch}
                          {if the prog gets here, the compared part was equal}
                          {so the longest string is the greatest}
        mov cl, [di-1]    {get length of first string (di has been switched}
                          {with si)}
        cmp cl, [si-1]    {compare with length of second string}
        jbe @noswitch     {length(string 1) < length(string 2) -> no switch}
       @switch:
        mov di, si
        db fs; db $66; ror word[bx+offset asort], 16
       @noswitch:
        sub bx,2          {decrease c2}
        jns @loop         {if above or equal 0 then loop}
        inc dx            {increase c1}
        mov bx, dx        {c2 = c1}
        add bx, bx
        cmp dx, ax        {compare c1 with pred(areas^.nofareas)}
        jbe @outloop      {if below or equal, loop}
       @end:
        pop ds
     End
End;

Procedure Update;
Const days : array [0..6] of String[9] =
           ('Sunday','Monday','Tuesday',
            'Wednesday','Thursday','Friday',
            'Saturday');
            areasstillactive: Word = 0;
            areasactivated: Word = 0;
            areasstillnoflow: Word = 0;
            areasnoflow: Word = 0;
            newareascount: Word = 0;

Var tempstr2: String;
    logfile: Text;
    dofw, d, m, y: Word;
    h,min,s: String[2];
    Newareas: Array[0..maxareas] of Word;
Begin
     Writeln('Writing new "Fidonet.na"...');
     Writeln;
     Assign(newfido, 'Newfido.na');
     Rewrite(NewFido);
     Assign(logfile, 'bbscan.log');
    {$i-}
     Append(logfile);
    {$i+}
     IF ioresult <> 0 Then Rewrite(logfile);
     If fidoexists Then
       Begin
         Readln(fido,tempstr);
         For c1 := 0 to areas^.nofareas Do
           Begin
              While ((tempstr < areas^.area[asort[c1]]) and not(eof(fido))) Do
                    Begin
                         If length(tempstr) <= l Then
                            Begin
                            Fillchar(tempstr[succ(length(tempstr))], l-length(tempstr), #$20);
                            tempstr[0] :=char(l);
                            tempstr := concat(tempstr, '[FiDo] No description available yet.')
                            End;
                         If tempstr[l+7] = ' ' Then
                            Begin
                                 inc(areasstillnoflow)
                            end
                          Else
                           Begin
                                inc(areasnoflow);
                                tempstr[l+7] := ' '
                           End;
                         Writeln(NewFido, tempstr);
                         ReadLn(fido, tempstr)
                    End;
              ASM
                 cld               {This part copies the areaname out of}
                 push ds           {tempstr to tempstr2.}
                 lea di, tempstr
                 pop es
                 mov al, ' '
                 xor bx, bx
                 mov bl, [es:di]
                 cmp bl, l+1
                 ja @length_ok
                 inc bl
                 mov [es:di+bx], al
                @length_ok:
                 inc di
                 mov cx, l+1
                 mov bx, l
                 repne scasb
                 sub bx, cx
                 push ss
                 mov cx, bx
                 lea si, tempstr+1
                 pop es
                 lea di, tempstr2
                 mov [es:di], cl
                 inc di
                 shr cx, 1
                 jnc @even
                 movsb
                @even:
                 rep movsw
              END;
              If tempstr2 = areas^.area[asort[c1]] Then
                 Begin
                      If length(tempstr) <= l Then
                         Begin
                              Fillchar(tempstr[succ(length(tempstr))],l-length(tempstr), #$20);
                              tempstr[0] := char(l);
                              tempstr := concat(tempstr, '[FiDo]*No description available yet.')
                              End;
                      If tempstr[l+7] = '*' Then inc(areasstillactive)
                         Else
                             Begin
                               tempstr[l+7] := '*';
                               inc(areasactivated)
                             End;
                      Writeln(NewFido, tempstr);
                      Readln(fido,tempstr)
                 End
               Else
                   Begin
                        newareas[newareascount] := c1;
                        inc(newareascount);
                        tempstr2 := areas^.area[asort[c1]];
                        For c2 := 1 to (l-length(areas^.area[asort[c1]])) Do
                            tempstr2 := concat(tempstr2,' ');
                        tempstr2 := concat(tempstr2, '[FiDo]*New added area. No description available yet.');
                        WriteLn(newfido,tempstr2)
                        End
           End
       End
      Else
         With areas^ Do
          Begin
              For c1 := 0 to nofareas Do
                  Begin
                       tempstr2 := area[asort[c1]];
                       For c2 := 1 to (l-length(area[asort[c1]])) Do
                            tempstr2 := concat(tempstr2,' ');
                        tempstr2 := concat(tempstr2, '[FiDo]*New added area. No description available yet.');
                        WriteLn(newfido,tempstr2)
                        End
       End;
     If fidoexists Then Writeln('"Fidonet.na" has been successfully updated!')
                    Else Writeln('"Fidonet.na" has been successfully created!');
                    Writeln;
     Writeln('Updating logfile (bbscan.log)...');
     Writeln;
     Getdate(y, m, d, dofw);
     Write(logfile,'---------- ',days[dofw],', ', d:0,'/',m:0,'/',y:0,', ');
     Gettime(y, m, d, dofw);
     str(y,h);
     str(m,min);
     str(d,s);
     If length(h) = 1 Then h := concat('0',h);
     If length(min) = 1 Then min := concat('0',min);
     If length(s) = 1 Then s := concat('0',s);
     Writeln(logfile, h,':',min,':',s,'.');
     If (newareascount > 0) Then
             Begin
                  Writeln(logfile, 'New Areas:');
                  For c1 := 0 to pred(newareascount) Do
                      Begin
                           Write(logfile, areas^.area[asort[newareas[c1]]]:38);
                           If (succ(c1) mod 2 = 0) Then Writeln(logfile)
                           End
             End;
     If (succ(c1) mod 2 <> 0) Then Writeln(logfile);
     Writeln(logfile);
     If not(fidoexists) Then newareascount := areas^.nofareas;
     Writeln(logfile, 'Amount of new areas:   ',newareascount);
     Writeln(logfile, 'Areas still active:    ',areasstillactive,'.');
     Writeln(logfile, 'Areas activated:       ',areasactivated,'.');
     Writeln(logfile, 'Areas still down:      ',areasstillnoflow,'.');
     Writeln(logfile, 'Areas deactivated:     ',areasnoflow,'.');
     Writeln(logfile, 'Total number of areas:',newareascount+areasstillactive+areasactivated+areasstillnoflow+areasnoflow,'.');
     Writeln(logfile);
     close(logfile);
     close(newfido);
     close(fido);
    {$i-}
     assign(logfile, 'fidonet.bak');
     Erase(logfile);
     rename(fido, 'fidonet.bak');
     rename(newfido, 'fidonet.na')
    {$i+}
End;

Begin
     Init;
     ReadareaNames;
     sort;
     update
END.