This file is indexed.

/usr/share/gap/lib/helpt2t.gi is in gap-libs 4r7p5-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
#############################################################################
##  
#W  helpt2t.gi                  GAP Library                 Frank Celler 
#W                                                          Alexander Hulpke
#W                                                          Greg Gamble
##  
##  
#Y  Copyright  (C)  1996-2001, Lehrstuhl  D  für  Mathematik, RWTH  Aachen,
#Y  Germany (C) 2001 School Math and  Comp. Sci., University of St Andrews,
#Y  Scotland
##  
##  The files  helpt2t.g{d,i} contain the  probably longest function  in the
##  GAP library. It converts TeX source code written in `gapmacro.tex' style
##  into text for the "screen" online help viewer.
##  
  
#############################################################################
##
#F  HELP_PRINT_SECTION( <book>, <chapter>, <section> [,<key>] ) . print entry
##
##  key is a function name
##  
##  main function to extract the help text for a topic from GAP main help
##  
HELP_FLUSHRIGHT:=true;
InstallGlobalFunction(HELP_PRINT_SECTION_TEXT, function(arg)
local   book, chapter, section, key, subkey, MatchKey, ssectypes,
	info, chap, filename, stream, p, q, lico, 
        line, i, j, lines, IsIgnoredLine, macro, macroarg, tail,
        ttenv, text, verbatim, nontex, item, initem, displaymath, align,
        lastblank, singleline, rund, blanks, SetArg, FlushLeft, Gather,
        width, buff, EmptyLine, ll, start, keynotfound, verb, URLends; 

  # flush buffer ... then add empty line
  EmptyLine := function()
    if Length(buff)>0 then
      Add(lines,buff);
      if keynotfound then start := start+1; fi;
      buff:="";
    fi;
    if not lastblank then
      Add(lines,"");
      if keynotfound then start := start+1; fi;
      lastblank:=true;
    fi;
  end;

  # get argument in {...} at front of tail (need to handle nested {}'s) 
  # ... macroarg is set with the argument matched and tail set to the rest
  SetArg := function()
    local  level, p;
    while 0<Length(tail) and tail[1]=' ' do
      tail := tail{[2..Length(tail)]};
    od;
    if IsEmpty(tail) then
      macroarg:="";
    elif tail[1]<>'{' then
      macroarg:=tail{[1]};
      tail := tail{[2..Length(tail)]};
    else
      level := 0;
      p := 2;
      while true do
        if p > Length(tail) then
          macroarg:=""; # Forget it ... can't match braces
          break;
        elif tail[p] = '{' then
          level := level+1;
        elif tail[p] = '}' then
          if level = 0 then 
            macroarg := tail{[2..p-1]};
            tail := tail{[p+1..Length(tail)]};
            break;
          else
            level := level-1;
          fi;
        fi;
        p := p+1;
      od;
    fi;
    return;
  end;

  FlushLeft := function(s)
    local p;
    p:=1;
    while p<=Length(s) and s[p]=' ' do
      p:=p+1;
    od;
    return s{[p..Length(s)]};
  end;

  IsIgnoredLine := function(line)
    # this is meant to catch lines that except for some initial
    # whitespace are of form: "\begingroup ... %"
    # or: "{\obeylines ... %" or: "{%" or "}%"
    if not displaymath and 1<Length(line) and line[Length(line)] = '%' then
      line := FlushLeft(line);
      return 1=Length(line) or 
             line{[1..Length(line)-1]} in ["{", "}"] or
             line{[1..2]}="{\\" and IsAlphaChar(line[3]) or
             line[1]='\\' and IsAlphaChar(line[2]);
    else
      return false;
    fi;
  end;

  # Scrubs a % from the beginning of the line in nontex mode
  # and joins lines ending in %
  Gather := function()
    local nextline;

    while line <> fail do
        if nontex and 0<Length(line) and line[1] = '%' then
            line := line{[2..Length(line)]};
        fi;
        if line="$$" then
            # toggle displaymath
            displaymath := not displaymath;
            align := false;
            EmptyLine();
        elif displaymath and 
             ((align and line="}") or (not align and line="\\matrix{")) then
            align := not align; # toggle align
        else
            break;
        fi;
        line := Chomp( ReadLine(stream) );
    od;
    # a '%' at end-of-line indicates a continuation
    while line<> fail and 0<Length(line) and line[Length(line)]='%' do
        line := line{[1..Length(line)-1]};
        repeat
            nextline := ReadLine(stream);
            if nontex and nextline<>fail and
               0<Length(nextline) and nextline[1] = '%' then
                nextline := nextline{[2..Length(nextline)]};
            fi;
        until nextline=fail or 0<Length(nextline) and nextline[1]<>'%';
        if nextline=fail then
            break;
        else
            nextline := Chomp(nextline);
        fi;
        line := Concatenation(line, FlushLeft(nextline));
    od;
  end;

  # Returns true if <s> (a string = <lico>) matches 
  # <type> (a ssectype of <key>), or else, false.
  # ... <type> is a list of strings that must match <s> in sequence, 
  # the following strings in <type> have special meaning:
  #  " " means skip blanks until the next non-blank and the
  #            next string must match at position 1.
  #  "^X" for any X, means scan what remains of string and
  #                        check there are no X chars.
  MatchKey := function(s, type)
    local p, q, str;
    q:=0;
    for str in type do
      if str=" " then
        s:=FlushLeft(s); q:=1;
      elif str[1]='^' then
        if Position(s, str[2]) <> fail then
          return false;
        fi;
        q:=0;
      else
        p:=PositionSublist(s, str);
        if p=fail or (q=1 and p<>1) then
          return false;
        fi;
        s:=s{[p+Length(str)..Length(s)]}; q:=0;
      fi;
    od;
    return true;
  end;

  width:=SizeScreen()[1]-6;
  book:=arg[1];
  chapter:=arg[2];
  section:=arg[3];

  # did we get the section only via a keyword?
  if Length(arg)>3 then
    key:=arg[4];
    p:=Position(key,'\!');
    if p=fail then
      subkey:="";
    else
      subkey:=key{[p+1..Length(key)]};
      key:=key{[1..p-1]};
    fi;
    # define subsection matching types: ssectypes
    # (ignoring any @... component)
    if subkey="" then
      ssectypes := 
        [ # \><key>(...)
          [ " ", key, " ", "(", ")", "^\!"],
          # \>`<key>' V
          [ Concatenation("`",key,"' "), "^{", " ", "V" ],
          # \>`...'{<key>}
          [ " ", "`", "'", " ", Concatenation("{",key,"}"), "^\!" ] ];
    else
      ssectypes := 
        [ # \><key>(...)!{<subkey>}
          [ " ", key, " ", "(", ")", " ", "\!", " ", 
            Concatenation("{", subkey, "}") ],
          # \>`...'{<key>!<subkey>}
          [ " ", "`", "'", " ", Concatenation("{",key,"\!",subkey,"}"), "^\!" ],
          # \>`...'{<key>}!{<subkey>}
          [ " ", "`", "'", " ", Concatenation("{",key,"}\!{",subkey,"}"), "^\!"]            ];
    fi;
  else
    key:=fail;
  fi;
  
  # get the chapter info
  info := HELP_BOOK_INFO(book);
  chap := HELP_CHAPTER_INFO( book, chapter );
  if chap = fail  then
      return;
  fi;

  # store lines
  lines := [];

  # open the stream and read in the help
  filename := Filename( info.directories, info.filenames[chapter] );
  stream := StringStreamInputTextFile(filename);
  if section = 0  then
      SeekPositionStream( stream, chap[1] );
      Add( lines, FILLED_LINE( info.chapters[chapter], info.bookname, '_' ) );
  else
      SeekPositionStream( stream, chap[2][section] );
      Add( lines, FILLED_LINE( info.sections[chapter][section],
                               info.chapters[chapter], '_' ) );
      #The repeat loop was a ``workaround'' for a deficiency in
      #`SeekPositionStream' which has now been fixed by FL
      #... so now `ReadLine' need only be executed once. - GG
      #repeat
        # On UNIX with files not in DOS format, stream positioning works
        #  and so we have the \Section... line on the first iteration.
        # On other architectures or with a DOS format file, stream positioning
        #  might get confused due to the CRLF problem (typically just one
        #  extra iteration is necessary to get past the LF). We continue
        #  reading until we know we have actually reached the line.
        line:=ReadLine(stream);
      #until MATCH_BEGIN( line, HELP_SECTION_BEGIN ) and
      #      # A section starts ... ensure it is the right section:
      #      MATCH_BEGIN( line{[10..Length(line)]}, 
      #                   info.sections[chapter][section] );
  fi;

  # Initialise a number of variables before the main section-scanning loop
  singleline:=0;     # 0 => paragraph-mode: append line to buff, when buff
                     #      gets longer than width, break into lines of
                     #      length width.
                     # 1 => obeylines-mode: simply add line to lines.
                     # 2 => obeylines-mode but ensuring no blank line is
                     #      inserted between lines.
  buff:="";          # In paragraph-mode, line is buffered in buff before
                     #  being broken into lines.
  lastblank:=false;  # Used to avoid having two blank lines in succession.
  start := 1;        # The section is scanned into lines, but displayed
                     #  from lines[start].

  nontex := false;   # Set inside %display{nontex} and %display{nonhtml}
                     #  environments, where after removal of an initial
                     #  % (if there is one), text is interpreted normally.
  # text-mode and verbatim-mode only differ in the way we treat "||"
  text:=false;       # Set inside %display{text}..%enddisplay
  verbatim := false; # Set inside each of \begintt..\endtt and 
                     #  \beginexample..\endexample
  ttenv:=false;      # Set inside \begintt..\endtt
  item := 0;         # Set to 1 in \beginitems..\enditems and 
                     #  to 1 (in \item entry) or 2 (in \itemitem entry)
                     #  of \beginlist..\endlist. Otherwise, set to 0.
  displaymath:=false;# Set inside $$ .. $$ for maths displays delimited
                     #  by $$ lines i.e. lines with *only* $$.
  align := false;    # Set inside displaymath in \matrix{ .. }
  keynotfound := false; # In case it is not set in the while loop

  line := ReadLine(stream);
  while line <> fail and not MATCH_BEGIN( line, HELP_SECTION_BEGIN ) do
      line := Chomp(line);

      if nontex and 0 < Length(line) and line[1] = '%' and 
         not MATCH_BEGIN( line, "%enddisplay" ) then
          line := line{[2..Length(line)]};
      fi;

      if key<>fail then 
          # we got the section only via a keyword. Ignore the first
          # part of the section and start only at the interesting
          # bits. When the key and subkey are found, key is set
          # to fail to disable skip mode i.e. key=fail => key found.

          # *Note* key and subkey have come from matches of (partial)
          # words (that the user entered) with the .six file,
          # so we need an *exact* match with key and subkey.
          # We do *not* use MATCH_BEGIN here.

          p:=PositionSublist(line,"\\>");
          if p<>fail then

              Gather(); # needed in case of continuations
              lico:=FlushLeft(NormalizedWhitespace(line{[p+2..Length(line)]}));
              if ForAny(ssectypes, type -> MatchKey(lico, type)) then
                  key := fail;
                  Info(InfoWarning, 2, "Matched line: ", line);
              fi;
          fi;
      fi;

      keynotfound := key<>fail; 
                
      # blanks lines are ok
      if 0 = Length(line)  then
          if not verbatim and not text then
              EmptyLine();
          fi;
      elif verbatim then
          if  MATCH_BEGIN(line,"\\endtt") then
              verbatim := false;
              ttenv := false;
              lastblank:=false;
              EmptyLine();
          elif  ( not ttenv and MATCH_BEGIN(line,"\\endexample") ) then
              verbatim := false;
              ttenv := false;
              lastblank:=false;
              buff :=  Concatenation (ListWithIdenticalEntries (width, "-"));
              EmptyLine();
          else
              lastblank:=true;
              # Any "||" actually represents "|"
              p := PositionSublist(line,"||");
              while p<> fail do
                  line:=Concatenation(line{[1..p]}, line{[p+2..Length(line)]});
                  p := PositionSublist(line,"||",p);
              od;
              Add( lines, line );
              if keynotfound then start := start+1; fi;
          fi;

      elif text then
          if MATCH_BEGIN(line,"%enddisplay")  then
              text := false;
              lastblank:=false;
              EmptyLine();
          elif line[1] = '%'  then
              Add( lines, line{[2..Length(line)]} );
              if keynotfound then start := start+1; fi;
          else
              lastblank:=false;
              Add( lines, line );
              if keynotfound then start := start+1; fi;
          fi;

      # ignore answers to exercises
      elif MATCH_BEGIN(line,"\\answer")  then
          repeat
              line := ReadLine(stream);
          until line = fail  or  line = "\n" or line = "\r\n";
                    
      # displays for text and HTML that need are interpreted
      # normally (except any initial % is first removed)
      elif MATCH_BEGIN(line,"%display{nontex}")
        or MATCH_BEGIN(line,"%display{nonhtml}") then
          nontex := true;
          text := false;
          verbatim := false;

      # ignore displays for TeX or HTML
      elif MATCH_BEGIN(line,"%display{tex}")
        or MATCH_BEGIN(line,"%display{nontext}")
        or MATCH_BEGIN(line,"%display{html}")
        or MATCH_BEGIN(line,"%display{jpeg}")  then
          repeat
              line := ReadLine(stream);
          until line = fail
             or MATCH_BEGIN(line,"%display{text}")
             or MATCH_BEGIN(line,"%display{nontex}")
             or MATCH_BEGIN(line,"%enddisplay");
          if MATCH_BEGIN(line,"%display{text}")  then
              text := true;
              EmptyLine();
          elif MATCH_BEGIN(line,"%display{nontex}") then
              nontex := true;
          fi;
                
      elif MATCH_BEGIN(line,"\\index{") 
        or MATCH_BEGIN(line,"\\indextt{") 
        or MATCH_BEGIN(line,"\\atindex{")  then
          # A '%' at end-of-line indicates a continuation
          while line <> fail and line <> "" and 
                (line[1] = '%' or line[Length(line)] = '%') do
              line := Chomp( ReadLine(stream) );
          od;
          line:="";

      # example environments
      elif MATCH_BEGIN(line,"\\beginexample") then
          verbatim := true;
          ttenv := false;
          EmptyLine();
          buff := Concatenation (
            Concatenation (ListWithIdenticalEntries (QuoInt (width-9, 2), "-")), 
            " Example ",
            Concatenation (ListWithIdenticalEntries (width - 9 - QuoInt (width-9, 2), "-")));  
          EmptyLine();
       elif MATCH_BEGIN(line,"\\begintt")  then
          ttenv := true;
          verbatim := true;
          EmptyLine();
       elif MATCH_BEGIN(line,"\\endexample") then  # Just in case ...
          verbatim := false;
          lastblank:=false;
          buff := Concatenation (ListWithIdenticalEntries (width-9, "-"));
          EmptyLine();
       elif  MATCH_BEGIN(line,"\\endtt")  then
          verbatim := false;
          lastblank:=false;
          EmptyLine();
      elif MATCH_BEGIN(line,"%display{text}")  then
          text := true;
          EmptyLine();
      elif MATCH_BEGIN(line,"%enddisplay")  then  # Just in case
          text := false;
          nontex := false;
          lastblank:=false;
          EmptyLine();
  
      elif MATCH_BEGIN(line,"\\beginitems") 
        or MATCH_BEGIN(line,"\\beginlist")  then
          item:=1;
          EmptyLine();

      elif MATCH_BEGIN(line,"\\enditems") 
        or MATCH_BEGIN(line,"\\endlist")  then
          item:=0;
          EmptyLine();
    
      # ignore lines beginning with '%' except if in text
      # or verbatim modes (which we have already dealt with)
      # or if in nontex mode (which we deal with below);
      # also ignore specific lines ending in a '%'
      # (other than the lines mentioned a '%' at end-of-line
      # indicates a continuation)
      elif not nontex and line[1] = '%' or
           # this is meant to exclude lines that after some
           # initial whitespace are of form: "\begingroup ... %"
           # or: "{\obeylines ... %" or "{%" or "}%"
           IsIgnoredLine(line) then
          ;

      # use everything else
      else
          if not text and not verbatim then
              Gather();

              if   MATCH_BEGIN(line,"\\exercise")  then
                  line{[1..9]} := "EXERCISE:";
              elif MATCH_BEGIN(line,"\\danger")  then
                  line{[1..7]} := "DANGER:";

              # cope with `\>' entries
              elif MATCH_BEGIN(line,"\\>") or
                   MATCH_BEGIN(line,"\\)") then
                  displaymath := false; # in case a $$ wasn't matched
                  if singleline<>2 then
                      # force separator if the line above was not
                      # already a header
                      EmptyLine();
                  fi;
                  singleline:=2; # we want it on a single line and it
                                 # may have a `cat' letter
                  rund:=line[2]=')';
                  line:=line{[3..Length(line)]}; # remove `>'
                  if rund then
                      if line[1]='{' then
                          tail:=line;
                          SetArg();
                          if macroarg="" then                 #No matching brace
                              line := line{[2..Length(line)]};#absorb '{' anyway
                          else
                              line := Concatenation(macroarg,"\\,",tail);
                          fi;
                      fi;
                      if 5<Length(line) and line{[1..6]}="\\fmark" then
                          line:=Concatenation(">",line{[7..Length(line)]});
                      fi;
                      # remove an \hfill if present
                      line := ReplacedString(line, "\\hfill", "");
                  else
                      while 0<Length(line) and line[1]=' ' do
                          line:=line{[2..Length(line)]};
                      od;
                      # remove @... label/index stuff from line
                      if 0<Length(line) and line[1]='`' then
                          # types: \>`...'{...}       
                          #        \>`...'{...}!{...} -> \>`...'{...!...}
                          #        \>`...'{...}@{...} -> \>`...'{...}
                          #        \>`...'{...}@`...' -> \>`...'{...}
                          p:=PositionSublist(line,"}!{");
                          if p<>fail then
                              line:=Concatenation(line{[1..p-1]},
                                                  line{[p+3..Length(line)]});
                          fi;
                          p:=Position(line,'@');
                          if p<>fail then
                              if p<Length(line) and line[p+1]='`' then
                                q:=Position(tail,'\'');
                                if q<>fail then
                                  line:=Concatenation(
                                            line{[1..p-1]},
                                            line{[q+1..Length(line)]});
                                fi;
                              else
                                tail:=line{[p+1..Length(line)]};
                                SetArg();                    # remove {...}
                                if macroarg<>"" then
                                  line:=Concatenation(line{[1..p-1]},tail);
                                fi;
                              fi;
                          fi;
                      fi;
                      line:=Concatenation("> ",line); # add the leading `>'
                  fi;
              elif displaymath then
                  singleline := 1;
                  if align then
                      # Should we check for \& here?
                      # ... my feeling is that \& should not be allowed
                      # (there are too many other places where it can
                      # cause problems) - GG
                      line := ReplacedString(line, "&", "");
                  fi;
              else
                  # by default we don't request single lines
                  singleline:=0;
              fi;

              # If we wanted to support \~ accents ...
              # (ties ~ as opposed to accents \~)
              #p:=Position(line,'~');
              #while p<>fail do
              #  if p=1 or line[p-1] <> '\\' then
              #    line[p]:=' ';
              #  fi;
              #  p:=Position(line,'~',p);
              #od;
                      
              if item>0 then
                  line:=FlushLeft(line);
                  p := Position(line,'&');
                  initem := p=fail;
                  if not initem then
                      if singleline=2 then
                          line[p] := ' ';
                      else
                          EmptyLine();
                          tail:= FlushLeft(line{[p+1..Length(line)]});
                          line:=Concatenation(line{[1..p-1]},"{\\break}",tail);
                          singleline:=0;
                      fi;
                  fi;
              fi;

              # some further handling of TeX macros
              p:=Position(line,'\\');
              while p<>fail do
                  tail:=line{[p+1..Length(line)]};
                  line:=line{[1..p-1]};
                  macroarg := "";
                  macro := "";
                  if tail = "" then
                      # if line ends in a \ indicating a continuation as
                      # ... we put it back
                      line[p] := '\\';
                      p := fail;
                  elif not IsAlphaChar(tail[1]) then
                      # Single character macros
                      macro := tail{[1]};
                      tail := tail{[2..Length(tail)]};
                      # escaped chars \ { } $ & _ % and <space>
                      if macro[1] in "\\{}$#&_% " then
                          ; 
                      # non-alpha accents 
                      elif macro[1] in "'`=^.\"~" then 
                          # \. could be an accent or multiplication
                          # ... we treat them the same. We don't support \~.
                          if Length(line)>0 and line[Length(line)]='{' and
                             Length(tail)>=2 and tail[2]='}' then
                              # We assume they appear as e.g.: {\'a}
                              line:=line{[1..Length(line)-1]};
                              macroarg:=tail{[1]};
                              tail:=tail{[3..Length(tail)]};
                          fi;
                          if macro = "=" then
                              macro := "-";
                          elif macro = "~" then
                              macro := ""; # just omit it
                          fi;
                      # fine spacing macros ... we just ignore
                      elif macro[1] in "!," then 
                          macro := "";
                      elif macro[1] in ";" then 
                          macro := " ";
                      # shouldn't get these here ...
                      elif macro[1] in ">)" then
                          macro := Concatenation(" ",macro);
                      # any we missed? ... just treat as escaped char
                      else
                          ;
                      fi;
                  else
                      # Multi-character macros
                      q:=1;
                      while q<=Length(tail) and IsAlphaChar(tail[q]) do
                          q:=q+1;
                      od;
                      macro:=tail{[1..q-1]};
                      tail:=tail{[q..Length(tail)]};
                      # accents have args ... deal with that
                      if macro in ["accent", "c", "d", "b",
                                        "t", "u", "v", "H"] then
                          if macro="accent" then
                              q:=1;
                              while q<=Length(tail) and IsDigitChar(tail[q]) do
                                q:=q+1;
                              od;
                              macro:=tail{[1..q-1]};
                              tail:=tail{[q..Length(tail)]};
                          fi;
                          if macro="19" and Length(tail)>=2 and
                             tail{[1..2]}="{}" then
                              tail:=tail{[3..Length(tail)]};
                          else
                              while 0<Length(tail) and tail[1]=' ' do
                                tail:=tail{[2..Length(tail)]};
                              od;
                              if 1<Length(tail) and macro="t" then
                                macroarg:=tail{[1..2]};
                                tail:=tail{[3..Length(tail)]};
                              elif 0<Length(tail) then
                                macroarg:=tail{[1]};
                                tail:=tail{[2..Length(tail)]};
                              fi;
                          fi;
                      fi;
                      # If macro enclosed in {..} or $..$ remove them
                      if  Length(line)>0 and Length(tail)>0 and
                          ((line[Length(line)]='{' and tail[1]='}') or
                           (line[Length(line)]='$' and tail[1]='$')) then
                          line:=line{[1..Length(line)-1]};
                          tail:=tail{[2..Length(tail)]};
                      fi;
                      # handle some macros
                      # all the \accentNNN accents including umlaut
                      # ... macro just contains NNN for these
                      if macro="18" then
                          macro:="`";
                      elif macro="19" then
                          macro:="'";
                      elif macro="20" then
                          macro:="\\v";
                      elif macro="21" then
                          macro:="\\u";
                      elif macro="22" then
                          macro:="-"; # not elegant
                      elif macro="94" then
                          macro:="^";
                      elif macro="95" then
                          macro:="."; # not elegant
                      elif macro="125" or macro="127" then  # umlaut
                          macro:="\"";
                      # We don't support \accent126 = \~
                      elif macro="126" then
                          macro:="";

                      # alpha accents
                      elif macro in [ "c", "d", "b", "t" ] then   # too hard
                          macro:="";                              # just omit
                      elif macro="u" or macro="v" then
                          # put backslash back
                          macro:=Concatenation("\\",macro);
                      elif macro="H" then # treat like umlaut
                          macro:="\"";
                          
                      # sharp s, ligatures, other specials
                      elif macro in [ "ss", "oe", "OE", "ae", "AE", 
                                      "o",  "O",  "l",  "L" , "i", "j"] then
                          ; # good enough without backslash
                      elif macro in [ "aa", "AA" ] then
                          macro := macro{[1]};

                      elif macro="copyright" then
                          macro := "(c)";

                      elif macro in ["GAP", "MOC", "CAS", "ATLAS"] then
                          ; # nothing to do it's right already
                      elif macro = "calR" then
                          macro := "R";
                      elif macro="package" then
                          SetArg(); # sets macroarg and tail as we need it
                          macro := "";
                      elif macro="lq" then
                          macro:="`";
                      elif macro="rq" then
                          macro:="'";
                      elif macro="pif" then
                          macro:="'";
                      elif macro in ["dots","ldots","cdots","vdots"] then
                          macro:="...";
                      elif macro="cdot" then
                          macro:=" . ";
                      elif macro="dot" then
                          macro:=".";

                      elif macro="kernttindent" then
                          macro:="";
                      elif macro="enspace" then
                          macro:="~";
                      elif macro="quad" then
                          macro:="~~";
                      elif macro="qquad" then
                          macro:="~~~";

                      elif macro="item" or macro="itemitem" then
                          EmptyLine();
                          SetArg();
                          singleline:=0;
                          initem:=false;
                          if macroarg="$-$" then
                              macroarg:="-";
                          fi;
                          # to get rid of any ordered list markup that
                          # is only interpreted for the HTML version
                          if macro="itemitem" then
                              q := Position(tail, '%');
                              if q <> fail then
                                  tail := tail{[1..q-1]};
                              fi;
                          fi;
                          # we do this so macroarg is scanned for macros
                          tail:=Concatenation(macroarg,"\\endmacro ",tail);
                          macroarg:="";
                          if macro="item" then
                              item:=1;
                              macro:="\\item";
                          else
                              item:=2;
                              macro:="~~~~\\item";
                          fi;

                      elif macro="endmacro" then
                          q:=PositionSublist(line,"\\item");
                          macroarg:=line{[q+5..Length(line)]};
                          line:=line{[1..q-1]};
                          macro:="";
                          tail:=FlushLeft(tail);
                          # we do this to prevent macroarg expanding
                          macroarg := ReplacedString(macroarg," ","~"); 
                          if Length(macroarg)>=3 then
                              macroarg:=Concatenation(macroarg,"~");
                          else
                              macroarg:=Concatenation(macroarg,"~~~~"){[1..4]};
                          fi;

                      # font changing commands
                      elif macro in ["bsf","sf","bf","rm","cal","sl","it"] then
                          if Length(line)>0 and line[Length(line)]='{' then
                              line:=line{[1..Length(line)-1]};
                              while 0<Length(tail) and tail[1]=' ' do
                                tail:=tail{[2..Length(tail)]};
                              od;
                              tail:=Concatenation("{",tail);
                              SetArg();
                              if macroarg<>"" then
                                if macro="bsf" then
                                  tail:=Concatenation("*",macroarg,"*",tail);
                                else
                                  tail:=Concatenation(macroarg,tail);
                                fi;
                                macroarg:="";
                              fi;
                          fi;
                          macro:="";
                      elif macro in [ "medskip",    "bigskip" ] then
                          macro:="";
                          EmptyLine();
       
                      elif macro in [ "par", "begingroup", "endgroup" ] then
                          tail := "";  # assume the rest of the line
                          macro := ""; # is not intended for our eyes

                      elif macro="hrule" then
                          line:= FILLED_LINE("","",'-');
                          line[1] := '-';
                          line[Length(line)] := '-';
                      elif macro="hfill" and align then
                          macro:="";
                      elif macro="hfill" then
                          line:= FILLED_LINE(line,tail,' ');
                      elif macro="break" then
                          line:= FILLED_LINE(line,"~ ",' ');
                          macro:="";
                      elif macro="cr" then
                          macro:="";

                      # math macros
                      elif macro="langle" or macro="lneqq" then
                          macro:="<";
                      elif macro="rangle" then
                          macro:=">";
                      elif macro="ne" or macro="neq" then
                          macro:=" <> ";
                      elif macro="le" or macro="leq" then
                          macro:=" <= ";
                      elif macro="ge" or macro="geq" then
                          macro:=" >= ";
                      elif macro="backslash" or macro="setminus" then
                          macro:="\\";
                      elif macro="bullet" then
                          macro:=".";
                      elif macro="circ" then
                          macro:="o";
                      elif macro="mapsto" then
                          macro:=" |-> ";
                      elif macro="longmapsto" then
                          macro:=" |--> ";
                      elif macro in ["rightarrow", "hookrightarrow", "to"] then
                          macro:=" -> ";
                      elif macro="Rightarrow" then
                          macro:=" => ";
                      elif macro="iff" then
                          macro:=" <=> ";
                      elif macro="vdash" then
                          macro:=" |- ";
                      elif macro="times" then
                          macro:=" x ";
                      elif macro="in" then
                          macro:=" in ";
                      elif macro="gamma" then
                          macro:="y"; # looks similar!
                      elif macro="forall" then
                          macro:=" for all ";
                      elif macro="exists" then
                          macro:=" there exists ";
                      elif macro="mid" then
                          macro:="|";
                      elif macro="colon" then
                          macro:=":";
                      elif macro="ast" then
                          macro:="*";
                      elif macro="lfloor" or macro="lbrack" then
                          macro:="[";
                      elif macro="rfloor" or macro="rbrack" then
                          macro:="]";
                      elif macro="left" or macro="right" then
                          macro:="";
                          if 0<Length(tail) and tail[1]='.' then
                              tail:=tail{[2..Length(tail)]};
                          fi;
                      elif macro="prime" then
                          # this isn't ideal since ^{\prime} becomes ^'
                          # ... but a_N^{\prime} becomes a_N^' at least
                          # showing that the ' belongs to a (rather than N)
                          macro:="'";
                      elif macro="cup" then
                          macro:=" U ";
                      elif macro="over" then
                          macro:="/";
                      elif macro="hbox" then
                          SetArg();
                          macro:="";
                      elif macro="frac" then
                          SetArg();
                          macro:=Concatenation(" ",macroarg,"/");
                          SetArg();
                          macro:=Concatenation(macro,macroarg," ");
                          macroarg:="";
                      elif macro="bmod" then
                          macro:=" mod ";
                      elif macro="pmod" then
                          SetArg();
                          macro:=Concatenation("(mod ",macroarg,")");
                          macroarg:="";
                      elif macro in [ "mathbin", "mathrel", "buildrel",
                                      "mathop",  "limits" ] then
                          macro:=""; # ignore
                      else
                          ; # display the macro name
                      fi;
                  fi;
                  if macro<>"hfill" and macro<>"hrule" then
                      line:=Concatenation(line,macro,macroarg,tail);
                  fi;
                  p := Length(line) - Length(tail);
                  if IsEmpty(line) then line:="";fi;
                  p:=Position(line,'\\',p);
              od;
          fi;

          lastblank:=false;
          if singleline=2 then
              while Length(line)>width do
                  p:=width;
                  while p>=1 and not(line[p] in " ,.>])") do
                      p:=p-1;
                  od;
                  if p = 0 then
                      p:=width-1;
                      if IsAlphaChar(line[p]) or IsDigitChar(line[p]) then
                          Add(lines, Concatenation(line{[1..p]}, "-"));
                      else
                          Add(lines, line{[1..p]});
                      fi;
                  elif line[p] = ' ' then
                      Add(lines, line{[1..p-1]});
                  else
                      Add(lines, line{[1..p]});
                  fi;
                  if keynotfound then start := start+1; fi;
                  line := Concatenation("~~~~", 
                                        FlushLeft(line{[p+1..Length(line)]}) );
              od;
              # treat trailing `category' letter(s)
              p:=Length(line);
              if 0<p and line[p] in "CROFPAMV" then
                  # we assume that the character before the category
                  # letter(s) is a blank space
                  if 1<p and line{[p-1..p]} = "AM" then
                      line:=FILLED_LINE(line{[1..p-2]},line{[p-2..p]},' ');
                  else
                      line:=FILLED_LINE(line{[1..p-1]},line{[p-1..p]},' ');
                  fi;
              fi;
              Add(lines,line);
              if keynotfound then start := start+1; fi;
          elif singleline=0 then
              if Length(buff)>0 and buff[Length(buff)] <> '~' then
                  Add(buff,' '); # separating ' '
              elif item>0 and initem and 0=Length(buff) and 
                   0<Length(line) and line[1]<>'~' then
                  buff:="~~~~";
              fi;
              buff:=Concatenation(buff,line);

              while Length(buff)>width do # force to fill lines
                  # find the last space to break
                  p:=width;
                  while p>=1 and buff[p] <> ' ' do
                      p:=p-1;
                  od;
                  if p=0 then
                      # cope with overfull lines
                      Add(lines,Concatenation(buff{[1..width-1]},"-"));
                      if keynotfound then start := start+1; fi;
                      buff:=buff{[width..Length(buff)]};
                  else
                      line:=buff{[1..p-1]};
                      buff:=buff{[p+1..Length(buff)]}; # letter p is the ' '

                      if HELP_FLUSHRIGHT and ' ' in line then
                          # remove leading and trailing blanks
			  ll:=1;
			  while ll<Length(line) and line[ll]=' ' do
			    ll:=ll+1;
			  od;
			  line:=line{[ll..Length(line)]};
                          ll:=Length(line);
                          while ll>0 and line[ll]=' ' do
                              ll:=ll-1;
                          od;
                          line:=line{[1..ll]};

                          # flush right adjustment, for this the line must
                          # contain spaces
                          p:=0;
                          while ll<width do
                              p:=Position(line,' ',p);
                              if p=fail then
                                # start anew
                                p:=Position(line,' ',0);
                              fi;
                              # if the line actually contains no
                              #  spaces then give up
                              if p = fail then
                                  break;
                              fi;    
                              # add a blank
                              line:=Concatenation(line{[1..p]}, line{[p..ll]});
                              # avoid to add the next blank just there
                              ll:=ll+1;
                              p:=p+1;
                              while p<=ll and line[p]=' ' do
                                p:=p+1;
                              od;
                              if p>ll then
                                p:=0;
                              fi;
                          od;
                      fi;

                      Add(lines,line);
                      if keynotfound then start := start+1; fi;
                      if item>0 then
                          buff := Concatenation( "~~~~", FlushLeft(buff) );
                          if item=2 then
                              buff := Concatenation("~~~~",buff);
                          fi;
                      fi;
                  fi;
              od;
          else
              if displaymath then
                  line := Concatenation("~~~~",line);
              fi;
              Add(lines,line);
              if keynotfound then start := start+1; fi;
          fi;
      fi;
      line := ReadLine(stream);
  od;
  CloseStream(stream);

  # Now we replace ~s by spaces, except in URLs and in `...'
  # (a TeX ~ is a tie which for on-line help amounts to an unstretchable space)
  verb := false; # Set to true inside `...' (named for LaTeX \verb|...|)
  for i in [1 .. Length(lines)] do
    # we assume URLs are not broken over lines, URLs don't contain `...'
    # environments and `...' environments don't contain URLs
    for j in [1 .. Length(lines[i])] do
      # replace a sequence of ~s at the beginning of a line by spaces
      if lines[i][j] = '~' then
        lines[i][j] := ' ';
      else
        break;
      fi;
    od;
    q := 0;
    if verb then
      q := Position(lines[i],''');
      while q<>fail and q<Length(lines[i]) and lines[i][q+1]=''' do
        q := Position(lines[i],''',q+1);
      od;
      if q=fail then
        q := Length(lines[i]);
      else
        verb := false;
      fi;
      tail := lines[i]{[q+1..Length(lines[i])]};
      line := ReplacedString(lines[i]{[1..q]},"~","\\tilde");
      q := Length(line);
      lines[i] := Concatenation(line,tail);
    fi;
    p := PositionSublist(lines[i],"URL{",q);
    URLends := [];
    while p<>fail do
      tail:=lines[i]{[p+3..Length(lines[i])]};
      line:=lines[i]{[1..p+2]};
      SetArg();
      if macroarg="" then # abort ... no matching '}'
        lines[i]:=Concatenation(line,tail);
        Add(URLends, [p, Length(lines[i])]);
        break;
      else
        lines[i]:=Concatenation(
                    line,"{",ReplacedString(macroarg,"~","\\tilde"),"}",tail);
      fi;
      Add(URLends, [p, Length(lines[i]) - Length(tail)]);
      p:=PositionSublist(lines[i],"URL{",Length(lines[i]) - Length(tail));
    od;
    p := q;
    while not verb and p < Length(lines[i]) do
      while p < Length(lines[i]) do
        # this loop sets p to the beginning of the next `...' environment
        # ... or sets p = fail if there isn't one
        p := Position(lines[i], '`', p);
        if p=fail then
          break;
        fi;
        while not IsEmpty(URLends) and p > URLends[1][2] do
          URLends := URLends{[2..Length(URLends)]}; #pop URLends
        od;
        # Have found a ` ... is it the beginning of a `...' environment?
        # It is if: it is not inside a URL, and 
        #           ` is not followed by ` unless its followed by ``.
        if IsEmpty(URLends) or p < URLends[1][1] then     # not inside a URL
          if p+1 < Length(lines[i]) and
             lines[i]{[p+1..p+2]} in ["`'", "''"] then
             # ``' or `'' (special cases: ` or ' inside `...')
            p := p + 2; # skip over (nothing to do)
          elif p = Length(lines[i]) or                      # ` (at end of line)
               lines[i][p+1]<>'`'   or                      # ` (on its own)
               p+1 < Length(lines[i]) and lines[i][p+2]='`' # ```
               then
            verb := true; 
            break;
          else # `` (but not ```) ... not a `...' environment 
            p := p + 1; # continue searching after second `
          fi;
        fi;
      od;  
      if not verb then # ... or equivalently: if p = fail
        break;
      fi;
      q := Position(lines[i],''',p);
      while q<>fail and q<Length(lines[i]) and lines[i][q+1]=''' do
        q := Position(lines[i],''',q+1);
      od;
      if q=fail then
        q := Length(lines[i]);
      else
        verb := false; # changed for when we finish this pass of the loop
      fi;
      tail := lines[i]{[q+1..Length(lines[i])]};
      line := Concatenation(lines[i]{[1..p]},
                            ReplacedString(lines[i]{[p+1..q]},"~","\\tilde"));
      lines[i] := Concatenation(line,tail);
      q := Length(line);
    od;  
    # at this point any ~ that should not be changed to a blank has been
    # temporarily replaced by: \tilde
    lines[i] := ReplacedString( lines[i], "~", " " );
    lines[i] := ReplacedString( lines[i], "\\tilde", "~" );
  od;

  EmptyLine();

  return rec(lines := lines, start := start);

end);

#############################################################################
##
#E