program HTML2Texy; uses dos; {pouzito pro moznost zjisteni existence souboru} {definice zacatku a koncu znacek} const ZPZ = '<'; ZDZ = ''; KZX = ' />'; MAXT = 0; {pripadna maximalni hloubka stromu} {definice polozky stromu} type PtrS = ^S; S = record name : string[20]; {jmeno znacky} istext : boolean; {priznak pro innerText} isattr : boolean; {priznak pro atribut} value : string; {hodnota innerTextu} child : PtrS; {ukazatel na potomka} next : PtrS; {ukazatel na sourozence} end; {definice zasobniku pro pridavani do stromu} type PtrL = ^Lifo; Lifo = record name : string[20]; {jmeno znacky} ptr : PtrS; {ukazatel ve stromu} next : PtrL; {ukazatel na dalsi polozku zasobniku} end; {definice promennych} var f,g : text; c : char; buffer : string; ground,prev,parent : PtrS; eolf : PtrL; ZRead : boolean; ul : boolean; cnt : longint; Ssoubor: string; Vsoubor: string; UVOZ : 34..39; {procedura pro smazani stromu HTML dokumentu} procedure DisposeTree(var ptr: PtrS); begin if ptr^.child <> NIL then DisposeTree(ptr^.child); {zavolani funkce na potomky} if ptr^.next <> NIL then DisposeTree(ptr^.next); {zavolani funkce na nasledovniky} dispose(ptr); {odstraneni z pameti} end; {procedura pro odebrani znacky ze zasobniku} procedure RemFromLifo; var actual : PtrL; begin actual := eolf; {writeln('odebiram ',actual^.name);{} if (actual = NIL) then begin {pokud by melo dojit k odebrani NILu ze zasobniku, je uzavreno vice znacek nez otevreno} {zpusobeno nevalidnim vstupem; doslo by k selhani programu} writeln('Vstupni soubor nebyl validni'); DisposeTree(ground); halt; end; eolf := actual^.next; dispose(actual); end; {procedura predchazejici zacykleni cyklu cekajicich na urcity znak} procedure NotValid; begin writeln('Zadany vstup neni validni.'); DisposeTree(ground); while (eolf <> NIL) do begin RemFromLifo; end; halt; end; {V pripade, ze ukonci beh programu, vstup nebyl validni} procedure CheckEof; begin if eof(f) then NotValid; end; {funkce zjistujici existenci souboru} function FileExists(FN :string) :boolean; var S : PathStr; begin S := FSearch(FN,''); {hledani v seznamu souboru} if S = '' then FileExists := false else FileExists := true; {dosazeni do funkce} end; {procedura pro otevreni vstupniho souboru} procedure OpenFile(var f: text; path : string); begin if FileExists(path) then {pokud soubor existuje, otevreme ho ke cteni} begin assign(f,path); reset(f); end else {v opacnem pripade ohlasime chybu a skoncime} begin writeln('CHYBA: Soubor nenalezen!'); halt; end; end; {procedura pro otevreni vystupniho souboru} procedure OpenVFile(var g: text; path : string); begin assign(g,path); rewrite(g); end; procedure CheckMem; begin {ochrana proti HEAP overflow, ktery je zapricinen prilis velkym vstupem} if (MaxAvail < 300) then begin writeln('Vstupni soubor obsahuje prilis mnoho dat.'); writeln('Rozdelte jej na nekolik mensich.'); DisposeTree(ground); while (eolf <> NIL) do begin RemFromLifo; end; halt; end; end; {procedura pro pridani polozky do stromu} procedure AddToTree(var ptr: PtrS; var name: string; istext: boolean; isattr: boolean; value: string; var parent: PtrS; var prev: PtrS); begin {nastaveni vlastnosti polozky} ptr^.name := name; ptr^.istext := istext; ptr^.isattr := isattr; ptr^.value := value; ptr^.child := NIL; ptr^.next := NIL; {pokud rodic jeste nema potomka, nastavi akt. prvek jako potomka} if (parent^.child = NIL) AND (parent <> NIL) then begin parent^.child := ptr; end; {pokud existuje predchazejici sourozenec, zaradi akt. prvek za nej} if (prev <> NIL) then begin prev^.next := ptr;{} end;{} {pokud je akt. prvek prvni polozkou stromu} if ground = NIL then begin ground := ptr; end; CheckMem; end; {procedura hledajici pozici, kam se bude zarazovat dalsi prvek} procedure GetTreePos(var rodic : PtrS; var sourozenec : PtrS); var temp : PtrS; begin rodic := NIL; sourozenec := NIL; if (eolf <> NIL) AND (eolf^.ptr <> NIL) then begin rodic := eolf^.ptr; {rodicem je konec zasobniku} {soourozencem je potomek rodice ukazujici na NIL} if eolf^.ptr^.child <> NIL then begin temp := eolf^.ptr^.child; while temp^.next <> NIL do begin temp := temp^.next; end; sourozenec := temp; end; end; end; {funkce, ktera ze stromu ziska hodnotu prislusneho atributu znacky} function GetAttrVal(var ptr : PtrS; attrname : string): string; var actual : PtrS; begin actual := ptr^.child; {pokud ma prvek potomky (atributy jsou mezi nimi)} while actual <> NIL do begin if (actual^.name = attrname) AND (actual^.isattr = true) then begin {hodnota atributu nalezena, dosadime do funkce a funkci ukoncime} GetAttrVal := actual^.value; exit; end; actual := actual^.next; end; GetAttrVal := '';{} end; {procedura pro rekurzivni prochazeni stromu a preklad do syntaxe Texy!} procedure TranslateTree(var ptr: PtrS; level : longint); var temp,temp2 : string; begin if MAXT > 0 then begin if level >= MAXT then begin writeln('CHYBA. Prilis velke zanoreni znacek.'); writeln('Rozdelte dokument do nekolika casti.'); DisposeTree(ground); halt; end; end; {znaky, ktere je treba vypsat pred innerTextem} if ptr^.isattr = false then begin if ptr^.name = 'b' then write(g,'**'); if ptr^.name = 'strong' then write(g,'**'); if ptr^.name = 'i' then write(g,'*'); if ptr^.name = 'em' then write(g,'*'); if ptr^.name = 'p' then write(g,chr(10),chr(13)); if ptr^.name = 'br' then write(g,chr(10),' '); if ptr^.name = 'hr' then write(g,chr(10),'--------'); if ptr^.name = 'h1' then write(g,chr(10),'======='); if ptr^.name = 'h2' then write(g,chr(10),'======'); if ptr^.name = 'h3' then write(g,chr(10),'====='); if ptr^.name = 'h4' then write(g,chr(10),'===='); if ptr^.name = 'h5' then write(g,chr(10),'==='); if ptr^.name = 'h6' then write(g,chr(10),'=='); if ptr^.name = 'title' then write(g,chr(10),chr(60),'!-- '); if ptr^.name = 'dd' then write(g,chr(10),' -'); if ptr^.name = 'dt' then write(g,chr(10)); if ptr^.name = 'tr' then writeln(g); if ptr^.name = 'td' then write(g,'| '); if ptr^.name = 'th' then write(g,'|* '); if ptr^.name = 'div' then begin write(g,chr(10),'/---div'); temp := GetAttrVal(ptr,'class');{} if temp <> '' then write(g,' .[',temp,']'); write(g,chr(10)); end; if ptr^.name = 'a' then write(g,' "'); if ptr^.name = 'img' then begin write(g,'[* '); write(g,GetAttrVal(ptr,'src'));{} end; if ptr^.name = 'sup' then write(g,'^'); if ptr^.name = 'sub' then write(g,'_'); if ptr^.name = 'ul' then ul := true; if ptr^.name = 'ol' then ul := false; if ptr^.name = 'li' then begin if ul = true then write(g,chr(10),'- ') else begin write(g,chr(10),cnt+1,') '); inc(cnt); end; end; end; {innerText} if ptr^.istext = true then write(g,ptr^.value); {zavolani funkce na potomky} if (ptr^.child <> NIL) then begin if ptr^.name <> 'script' then begin TranslateTree(ptr^.child,level+1); end; end; {znaky, ktere je treba vypsat za innerTextem} if ptr^.isattr = false then begin if ptr^.name = 'b' then write(g,'**'); if ptr^.name = 'strong' then write(g,'**'); if ptr^.name = 'i' then write(g,'*'); if ptr^.name = 'em' then write(g,'*'); if ptr^.name = 'title' then write(g,' --',chr(62),chr(10)); if ptr^.name = 'acronym' then write(g,'((',GetAttrVal(ptr,'title'),'))'); if ptr^.name = 'div' then write(g,chr(10),'\--- ',chr(10)); if ptr^.name = 'dt' then write(g,':'); if (ptr^.name = 'td') OR (ptr^.name = 'th') then begin write(g,' '); end; if ptr^.name = 'a' then begin write(g,'":'); write(g,GetAttrVal(ptr,'href'),' '); end; if ptr^.name = 'img' then begin temp := GetAttrVal(ptr,'width');{} temp2 := GetAttrVal(ptr,'height');{} if (temp <> '') AND (temp2 <> '') then begin write(g,' ',temp,'x',temp2); end; write(g,' .(',GetAttrVal(ptr,'alt'),')'); temp := GetAttrVal(ptr,'class'); if temp <> '' then write(g,'[',temp,']');{} temp := GetAttrVal(ptr,'align');{} if temp = '' then write(g,' *] '); if temp = 'left' then write(g,' <] '); if temp = 'right' then write(g,' >] '); end; if ptr^.name = 'ul' then ul := false; if ptr^.name = 'ol' then begin ul := false; cnt := 0; end; if ptr^.name = 'p' then begin temp := GetAttrVal(ptr,'align'); if temp = 'left' then write(g,' .<'); if temp = 'right' then write(g,' .>'); if temp = 'center' then write(g,' .<>'); end; end; {zavolani funkce na sourozence} if ptr^.next <> NIL then TranslateTree(ptr^.next,level); end; {procedura pro testovaci vypis struktury stromu} procedure FetchTreeItem(var ptr: PtrS; level : longint); var i : longint; begin for i := level downto 2 do write(' '); if level > 0 then write(chr(192)); write(ptr^.name);{} if ptr^.isattr = true then write(' *atrr (',ptr^.value,')'); if ptr^.istext = true then write(' * (',ptr^.value,')'); writeln; if ptr^.child <> NIL then FetchTreeItem(ptr^.child,level+1); {zavolani funkce na potomky} if ptr^.next <> NIL then FetchTreeItem(ptr^.next,level); {zavolani funkce na sourozence} end; {procedura, ktera prida znacku do zasobniku a zavola jeji pridani do stromu} procedure AddToLifo(var name : string; var ptrI: PtrS); var actual : PtrL; temp,sourozenec,rodic : PtrS; begin new(actual); {nove misto v pameti} writeln('pridavam ',name);{} rodic := NIL; sourozenec := NIL; GetTreePos(rodic,sourozenec); {ziskani pozice ve stromu} if eolf <> NIL then rodic := eolf^.ptr else rodic := NIL; {writeln(name,rodic^.name,sourozenec^.name);{} AddToTree(ptrI,name,false,false,'',rodic,sourozenec);{} actual^.name := name; {nastaveni vlastnosti} actual^.ptr := ptrI; actual^.next := eolf; eolf := actual; {novy konec zasobniku} end; {procedura pro ziskani hodnoty atributu, ktera posleze zavola pridani atributu do stromu} procedure ParseAttrs(var buffer: string); var c,name,val : string; index : integer; sourozenec, rodic, item : PtrS; begin c := ''; name := ''; val := ''; index := 1; sourozenec := NIL; rodic := NIL; {pokud dostaneme na vstpu validni znaky pro atributy} if (buffer <> '/') AND (buffer <> '') then begin CheckMem; new(item); {rezervujeme misto v pameti} while (c <> '=') do {pred znakem = hledame jmeno atributu} begin name := name + c; c := copy(buffer,index,1); inc(index); if index >= length(buffer) then begin NotValid; end; end; inc (index); val := copy(buffer,index,length(buffer)-index+1); {vsechno dalsi je hodnota atributu} GetTreePos(rodic,sourozenec); {dosazeni pozice ve stromu} AddToTree(item,name,false,true,val,rodic,sourozenec); {pridani do stromu} {writeln('pridavam atribut ',name);{} end; buffer := ''; end; {procedura, ktera hleda ve znacce atributy a predava je k rozparsovani} procedure ReadAttrs(var f : text; var c : char; var eot: boolean); var buffer : string; openattr : boolean; index : integer; backslash: boolean; begin buffer := ''; index := 0; openattr := false; backslash := false; while c <> KZ do {dokud nenarazime na konec znacky, hledame atributy} begin {write(c);{} if ((c <> ' ') AND (openattr = false)) OR ((c <> chr(UVOZ)) AND (openattr = true)) then begin if (c <> chr(10)) AND (c <> chr(13)) AND (c <> chr(9)) then buffer := buffer + c; {pokud je atribut delsi nez 255 znaku, je treba jej rozdelit do vice polozek} {takove atributy vsak nejsou prekladany, nebot je syntaxe texy nezna} if index = 254 then begin ParseAttrs(buffer); buffer := '='+chr(UVOZ); index := 1; end; if ((c = chr(UVOZ)) AND (backslash = false)) then openattr := NOT openattr; inc(index); end else begin if (backslash = false) then begin ParseAttrs(buffer); openattr := false; end else buffer := buffer+c; end; if c = '\' then backslash := true else backslash := false; read(f,c); CheckEof; end; ZRead := true; if buffer = '/' then eot := true; if buffer <> '' then ParseAttrs(buffer); end; {procedura pro precteni innerTextu - textu mezi znackami} procedure ReadInner(var f : text; var c : char); var buffer,name : string; index : integer; rodic, sourozenec,item : PtrS; begin buffer := ''; name := 'innerText'; ZRead := false; index := 0; read(f,c); {writeln('jsem zavolan na ',eolf^.ptr^.name,'|');{} {write(c);{} {osetreni proti nezakomentovanemu script se znakem <} if (eolf^.ptr^.name = 'script') then begin while (copy(buffer,index-9,9) <> '') do begin buffer := buffer+c; if index = 254 then begin index := 9; buffer := copy(buffer,245,9); {v bufferu mohu mit cast hledaneho vyrazu} end; read(f,c); inc(index); end; exit; end; {osetreni proti nezakomentovanemu style se znakem <} if (eolf^.ptr^.name = 'style') then begin while (copy(buffer,index-8,8) <> '') do begin buffer := buffer+c; if index = 254 then begin index := 8; buffer := copy(buffer,246,8); end; read(f,c); inc(index); end; exit; end; {dokud nenarazim na dalsi znacku, ctu hodnotu innerTextu} while ((c <> ZPZ) AND (NOT(eof(f)))) do begin if (c <> chr(10)) AND (c <> chr(13)) AND (c <> chr(9)) then buffer := buffer+c; {pokud je innerText delsi nez 255 znaku, je treba ho rozdelit do vice polozek} if index = 254 then begin CheckMem; new(item); GetTreePos(rodic,sourozenec); AddToTree(item,name,true,false,buffer,rodic,sourozenec); {pridani polozky do stromu} index := -1; buffer := ''; end; read(f,c); {write(ord(c),' ');{} inc(index); end; ZRead := true; if (buffer <> ' ') AND (buffer <> '') then begin {writeln(buffer,' napsal inner');{} new(item); GetTreePos(rodic,sourozenec); {writeln('~',name,rodic^.name,sourozenec^.name);{} AddToTree(item,name,true,false,buffer,rodic,sourozenec);{} end; end; {procedura, ktera hleda nazvy znacek a vola ReadInner a ReadAttrs} procedure ParseHTML(var f : text); var cbn,eot,added : boolean; name : string; treeItem : PtrS; begin buffer := ''; added := false; while not eof(f) do {dokud nejsem na konci souboru} begin cbn := true; eot := false; {boolean ZRead je nastaven procedurou ReadInner, ktera precte zacatecni znak znacky} if ZRead = false then read(f,c) else ZRead := false; {nacti znak} {hromadeni ziskane hodnoty} buffer := buffer + c; added := false; if c = ZPZ then {pokud je to zacatek znacky} begin {if (buffer <> '') AND (buffer <> ZPZ) then writeln(buffer);{} buffer := ''; read(f,c); {cti znak} if c = '/' then {pokud je znacka ukoncujici, nastav eot na true} begin eot := true; read(f,c); end; CheckMem; new(treeItem); while c <> KZ do {precti celou znacku} begin if (c = ' ') then {pokud narazis na mezeru} begin if cbn = true then {pokud je to prvni mezera ve znacce} begin {writeln(name);{} name := buffer; {aktualni sbirka znaku je nazev znacky} cbn := false; {zname jmeno} {DOCTYPE a poznamky jsou pro dokument nepodstatne a vymykaji se beznemu formatu} if (name <> '!DOCTYPE') AND (copy(name,1,3) <> '!--') AND (copy(name,1,1) <> '?') then begin AddToLifo(name,treeItem); {pridej do zasobniku} ReadAttrs(f,c,eot); {precti atributy znacky} end; added := true; end; end; buffer := buffer + c; {cti dal} {boolean ZRead nastavuje procedura ReadAttrs, ktera jako posledni precte znak} {ktery je zde potrebny a jinak by doslo k jeho zahozeni} if ZRead = false then read(f,c) else ZRead := false; {CheckEof;{} end; if cbn = true then name := buffer; {pokud ve znacce nebyla mezera, je cely jeji obsah jmeno} {writeln(buffer,' ',name);{} {pokud nemame znacku odebrat, pridame ji...} if NOT eot then begin {... pokud nebyla pridana vyse v kodu (obsahovala mezeru)} if NOT added then AddToLifo(name,treeItem); end; {pokud je znacka ukoncujici nebo neparova, odebereme ze zasobniku} if ((eot) OR (copy(buffer,length(buffer),1) = '/')) then RemFromLifo;{} {odeber ze zasobniku} {writeln('volam inner');{} ReadInner(f,c);{} buffer := ''; end; end; end; {incializace programu} begin ul := false; cnt := 0; ZRead := false; writeln('HTML2Texy! prevadec');{} writeln;{} {ziskani cesty ke vstupnimu souboru} writeln('Zadejte cestu ke vstupnimu HTML souboru:');{} readln(Ssoubor); {Ssoubor := 'C:\Pascal\error.htm';{} OpenFile(f,Ssoubor);{} {ziskani cesty k vystupnimu souboru} writeln('Zadejte cestu k vystupnimu HTML souboru:');{} readln(Vsoubor); {Vsoubor := 'C:\abcd.tex';{} OpenVFile(g,Vsoubor);{} {Nektere stranky chybne pouzivaji misto uvozovek apostrofy} writeln('Zadejte format uvozovek:'); writeln(' 34 pro znak ',chr(34)); writeln(' 39 pro znak ',chr(39));{} readln(UVOZ);{} {snilovani ukazatelu} ground := NIL; parent := NIL; prev := NIL; eolf := NIL; {spusteni hlavni procedury pro vytvoreni stromu} ParseHTML(f);{} {uzavreni vstupniho souboru} close(f); {FetchTreeItem(ground,0);{} {vypis stromu do Texy!} TranslateTree(ground,0);{} {Uvolneni pameti} DisposeTree(ground);{} {uzavreni vystupniho souboru} close(g); end.