2
0

msg2inc.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. {
  2. This program is part of the Free Pascal run time library.
  3. Copyright (c) 1998-2002 by Peter Vreman
  4. Convert a .msg file to an .inc file with a const array of char
  5. And for the lazy docwriters it can also generate some TeX output
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. program msg2inc;
  13. uses
  14. strings;
  15. {$ifdef unix}
  16. {$define EOL_ONE_CHAR}
  17. {$endif unix}
  18. {$ifdef amiga}
  19. {$define EOL_ONE_CHAR}
  20. {$endif amiga}
  21. {$ifdef morphos}
  22. {$define EOL_ONE_CHAR}
  23. {$endif}
  24. {$ifdef macos}
  25. {$define EOL_ONE_CHAR}
  26. {$endif}
  27. const
  28. version='1.00';
  29. {$ifdef EOL_ONE_CHAR}
  30. eollen=1;
  31. {$else}
  32. eollen=2;
  33. {$endif}
  34. msgparts = 20;
  35. type
  36. TMode=(M_Char,M_Tex,M_Intel,M_String,M_Renumber);
  37. var
  38. InFile,
  39. OutFile,
  40. OutName : string;
  41. Mode : TMode;
  42. TexHeader : boolean;
  43. MsgTxt : pchar;
  44. EnumTxt : pchar;
  45. enumsize,
  46. msgsize : longint;
  47. msgidxmax : array[1..msgparts] of longint;
  48. msgs : array[0..msgparts,0..999] of boolean;
  49. procedure LoadMsgFile(const fn:string);
  50. var
  51. f : text;
  52. error,
  53. multiline : boolean;
  54. code : word;
  55. numpart,numidx,
  56. line,i,j,num : longint;
  57. ptxt,
  58. penum : pchar;
  59. number,
  60. s,s1 : string;
  61. procedure err(const msgstr:string);
  62. begin
  63. writeln('error in line ',line,': ',msgstr);
  64. error:=true;
  65. end;
  66. begin
  67. Writeln('Loading messagefile ',fn);
  68. {Read the message file}
  69. assign(f,fn);
  70. {$push} {$I-}
  71. reset(f);
  72. {$pop}
  73. if ioresult<>0 then
  74. begin
  75. WriteLn('fatal error: '+fn+' not found');
  76. halt(1);
  77. end;
  78. { First parse the file and count bytes needed }
  79. fillchar(msgidxmax,sizeof(msgidxmax),0);
  80. fillchar(msgs,sizeof(msgs),0);
  81. error:=false;
  82. line:=0;
  83. multiline:=false;
  84. msgsize:=0;
  85. while not eof(f) do
  86. begin
  87. readln(f,s);
  88. inc(line);
  89. if multiline then
  90. begin
  91. if s=']' then
  92. multiline:=false
  93. else
  94. inc(msgsize,length(s)+1); { +1 for linebreak }
  95. end
  96. else
  97. begin
  98. if (s<>'') and not(s[1] in ['#',';','%']) then
  99. begin
  100. i:=pos('=',s);
  101. if i>0 then
  102. begin
  103. j:=i+1;
  104. if not(s[j] in ['0'..'9']) then
  105. err('no number found')
  106. else
  107. begin
  108. while (s[j] in ['0'..'9']) do
  109. inc(j);
  110. end;
  111. if j-i-1<>5 then
  112. err('number length is not 5');
  113. number:=Copy(s,i+1,j-i-1);
  114. { update the max index }
  115. val(number,num,code);
  116. numpart:=num div 1000;
  117. if numpart=0 then
  118. err('number should be > 1000');
  119. numidx:=num mod 1000;
  120. { duplicate ? }
  121. if msgs[numpart,numidx] then
  122. err('duplicate number found');
  123. msgs[numpart,numidx]:=true;
  124. { check range }
  125. if numpart > msgparts then
  126. err('number is to large')
  127. else
  128. if numidx > msgidxmax[numpart] then
  129. msgidxmax[numpart]:=numidx;
  130. if s[j+1]='[' then
  131. begin
  132. inc(msgsize,j-i);
  133. multiline:=true
  134. end
  135. else
  136. inc(msgsize,length(s)-i+1);
  137. inc(enumsize,j);
  138. end
  139. else
  140. err('no = found');
  141. end;
  142. end;
  143. end;
  144. if multiline then
  145. err('still in multiline mode');
  146. if error then
  147. begin
  148. close(f);
  149. writeln('aborting');
  150. halt(1);
  151. end;
  152. { alloc memory }
  153. getmem(msgtxt,msgsize);
  154. { no linebreak after last entry }
  155. dec(msgsize);
  156. ptxt:=msgtxt;
  157. getmem(enumtxt,enumsize);
  158. penum:=enumtxt;
  159. { now read the buffer in mem }
  160. reset(f);
  161. while not eof(f) do
  162. begin
  163. readln(f,s);
  164. if multiline then
  165. begin
  166. if s=']' then
  167. begin
  168. multiline:=false;
  169. { overwrite last eol }
  170. dec(ptxt);
  171. ptxt^:=#0;
  172. inc(ptxt);
  173. end
  174. else
  175. begin
  176. move(s[1],ptxt^,length(s));
  177. inc(ptxt,length(s));
  178. ptxt^:=#10;
  179. inc(ptxt);
  180. end;
  181. end
  182. else
  183. begin
  184. if (s<>'') and not(s[1] in ['#',';','%']) then
  185. begin
  186. i:=pos('=',s);
  187. if i>0 then
  188. begin
  189. j:=i+1;
  190. while (s[j] in ['0'..'9']) do
  191. inc(j);
  192. {enum}
  193. move(s[1],penum^,i-1);
  194. inc(penum,i-1);
  195. penum^:='=';
  196. inc(penum);
  197. number:=Copy(s,i+1,j-i-1);
  198. move(number[1],penum^,length(number));
  199. inc(penum,length(number));
  200. penum^:=#0;
  201. inc(penum);
  202. { multiline start then no txt }
  203. if s[j+1]='[' then
  204. begin
  205. s1:=Copy(s,i+1,j-i);
  206. move(s1[1],ptxt^,length(s1));
  207. inc(ptxt,length(s1));
  208. multiline:=true;
  209. end
  210. else
  211. begin
  212. { txt including number }
  213. s1:=Copy(s,i+1,255);
  214. move(s1[1],ptxt^,length(s1));
  215. inc(ptxt,length(s1));
  216. ptxt^:=#0;
  217. inc(ptxt);
  218. end;
  219. end;
  220. end;
  221. end;
  222. end;
  223. close(f);
  224. end;
  225. {*****************************************************************************
  226. WriteEnumFile
  227. *****************************************************************************}
  228. procedure WriteEnumFile(const fn,typename:string);
  229. var
  230. t : text;
  231. i : longint;
  232. p : pchar;
  233. start : boolean;
  234. begin
  235. writeln('Writing enumfile '+fn);
  236. {Open textfile}
  237. assign(t,fn);
  238. rewrite(t);
  239. writeln(t,'const');
  240. {Parse buffer in msgbuf and create indexs}
  241. p:=enumtxt;
  242. start:=true;
  243. for i:=1 to enumsize do
  244. begin
  245. if start then
  246. begin
  247. write(t,' ');
  248. start:=false;
  249. end;
  250. if p^=#0 then
  251. begin
  252. writeln(t,';');
  253. start:=true;
  254. end
  255. else
  256. begin
  257. write(t,p^);
  258. end;
  259. inc(p);
  260. end;
  261. writeln(t,'');
  262. { msgtxt size }
  263. writeln(t,' MsgTxtSize = ',msgsize,';');
  264. writeln(t,'');
  265. { max msg idx table }
  266. writeln(t,' MsgIdxMax : array[1..20] of longint=(');
  267. write(t,' ');
  268. for i:=1 to 20 do
  269. begin
  270. write(t,msgidxmax[i]+1);
  271. if i<20 then
  272. write(t,',');
  273. if i=10 then
  274. begin
  275. writeln(t,'');
  276. write(t,' ');
  277. end;
  278. end;
  279. writeln(t,'');
  280. writeln(t,' );');
  281. close(t);
  282. end;
  283. {*****************************************************************************
  284. WriteStringFile
  285. *****************************************************************************}
  286. procedure WriteStringFile(const fn,constname:string);
  287. const
  288. maxslen=240; { to overcome aligning problems }
  289. function l0(l:longint):string;
  290. var
  291. s : string[16];
  292. begin
  293. str(l,s);
  294. while (length(s)<5) do
  295. s:='0'+s;
  296. l0:=s;
  297. end;
  298. var
  299. t : text;
  300. f : file;
  301. slen,
  302. len,i : longint;
  303. p : pchar;
  304. s : string;
  305. start,
  306. quote : boolean;
  307. begin
  308. writeln('Writing stringfile ',fn);
  309. {Open textfile}
  310. assign(t,fn);
  311. rewrite(t);
  312. writeln(t,'{$ifdef Delphi}');
  313. writeln(t,'const '+constname+' : array[0..000000] of string[',maxslen,']=(');
  314. writeln(t,'{$else Delphi}');
  315. writeln(t,'const '+constname+' : array[0..000000,1..',maxslen,'] of char=(');
  316. write(t,'{$endif Delphi}');
  317. {Parse buffer in msgbuf and create indexs}
  318. p:=msgtxt;
  319. slen:=0;
  320. len:=0;
  321. quote:=false;
  322. start:=true;
  323. for i:=1 to msgsize do
  324. begin
  325. if slen>=maxslen then
  326. begin
  327. if quote then
  328. begin
  329. write(t,'''');
  330. quote:=false;
  331. end;
  332. write(t,',');
  333. slen:=0;
  334. inc(len);
  335. end;
  336. if (len>70) or (start) then
  337. begin
  338. if quote then
  339. begin
  340. write(t,'''');
  341. quote:=false;
  342. end;
  343. if slen>0 then
  344. writeln(t,'+')
  345. else
  346. writeln(t);
  347. len:=0;
  348. start:=false;
  349. end;
  350. if (len=0) then
  351. write(t,' ');
  352. if (ord(p^)>=32) and (p^<>#39) then
  353. begin
  354. if not quote then
  355. begin
  356. write(t,'''');
  357. quote:=true;
  358. inc(len);
  359. end;
  360. write(t,p^);
  361. inc(len);
  362. end
  363. else
  364. begin
  365. if quote then
  366. begin
  367. write(t,'''');
  368. inc(len);
  369. quote:=false;
  370. end;
  371. write(t,'#'+chr(ord(p^) div 100+48)+chr((ord(p^) mod 100) div 10+48)+chr(ord(p^) mod 10+48));
  372. inc(len,3);
  373. end;
  374. if p^ in [#0,#10] then
  375. start:=true;
  376. inc(slen);
  377. inc(p);
  378. end;
  379. if quote then
  380. write(t,'''');
  381. writeln(t,'');
  382. writeln(t,');');
  383. close(t);
  384. {update arraysize}
  385. s:=l0(msgsize div maxslen); { we start with 0 }
  386. assign(f,fn);
  387. reset(f,1);
  388. seek(f,34+eollen+length(constname));
  389. blockwrite(f,s[1],5);
  390. seek(f,90+3*eollen+2*length(constname));
  391. blockwrite(f,s[1],5);
  392. close(f);
  393. end;
  394. {*****************************************************************************
  395. WriteCharFile
  396. *****************************************************************************}
  397. procedure WriteCharFile(const fn,constname:string);
  398. function l0(l:longint):string;
  399. var
  400. s : string[16];
  401. begin
  402. str(l,s);
  403. while (length(s)<5) do
  404. s:='0'+s;
  405. l0:=s;
  406. end;
  407. function createconst(b:byte):string;
  408. begin
  409. if (b in [32..127]) and (b<>39) then
  410. createconst:=''''+chr(b)+''''
  411. else
  412. createconst:='#'+chr(b div 100+48)+chr((b mod 100) div 10+48)+chr(b mod 10+48)
  413. end;
  414. var
  415. t : text;
  416. f : file;
  417. cidx,i : longint;
  418. p : pchar;
  419. s : string;
  420. begin
  421. writeln('Writing charfile '+fn);
  422. {Open textfile}
  423. assign(t,fn);
  424. rewrite(t);
  425. writeln(t,'const ',constname,' : array[1..00000] of char=(');
  426. {Parse buffer in msgbuf and create indexs}
  427. p:=msgtxt;
  428. cidx:=0;
  429. for i:=1to msgsize do
  430. begin
  431. if cidx=15 then
  432. begin
  433. if cidx>0 then
  434. writeln(t,',')
  435. else
  436. writeln(t,'');
  437. write(t,' ');
  438. cidx:=0;
  439. end
  440. else
  441. if cidx>0 then
  442. write(t,',')
  443. else
  444. write(t,' ');
  445. write(t,createconst(ord(p^)));
  446. inc(cidx);
  447. inc(p);
  448. end;
  449. writeln(t,');');
  450. close(t);
  451. {update arraysize}
  452. s:=l0(msgsize);
  453. assign(f,fn);
  454. reset(f,1);
  455. seek(f,18+length(constname));
  456. blockwrite(f,s[1],5);
  457. close(f);
  458. end;
  459. {*****************************************************************************
  460. WriteIntelFile
  461. *****************************************************************************}
  462. procedure WriteIntelFile(const fn,constname:string);
  463. var
  464. t : text;
  465. len,i : longint;
  466. p : pchar;
  467. start,
  468. quote : boolean;
  469. begin
  470. writeln('Writing Intelfile ',fn);
  471. {Open textfile}
  472. assign(t,fn);
  473. rewrite(t);
  474. writeln(t,'procedure '+constname+';assembler;');
  475. writeln(t,'asm');
  476. {Parse buffer in msgbuf and create indexs}
  477. p:=msgtxt;
  478. len:=0;
  479. start:=true;
  480. quote:=false;
  481. for i:=1to msgsize do
  482. begin
  483. if len>70 then
  484. begin
  485. if quote then
  486. begin
  487. write(t,'''');
  488. quote:=false;
  489. end;
  490. writeln(t,'');
  491. start:=true;
  492. end;
  493. if start then
  494. begin
  495. write(t,' db ''');
  496. len:=0;
  497. quote:=true;
  498. end;
  499. if (ord(p^)>=32) and (p^<>#39) then
  500. begin
  501. if not quote then
  502. begin
  503. write(t,',''');
  504. quote:=true;
  505. inc(len);
  506. end;
  507. write(t,p^);
  508. inc(len);
  509. end
  510. else
  511. begin
  512. if quote then
  513. begin
  514. write(t,'''');
  515. inc(len);
  516. quote:=false;
  517. end;
  518. write(t,','+chr(ord(p^) div 100+48)+chr((ord(p^) mod 100) div 10+48)+chr(ord(p^) mod 10+48));
  519. inc(len,4);
  520. end;
  521. inc(p);
  522. end;
  523. if quote then
  524. write(t,'''');
  525. writeln(t,'');
  526. writeln(t,'end;');
  527. close(t);
  528. end;
  529. {*****************************************************************************
  530. RenumberFile
  531. *****************************************************************************}
  532. procedure RenumberFile(const fn,name:string);
  533. var
  534. f,t : text;
  535. i : longint;
  536. s,s1 : string;
  537. begin
  538. Writeln('Renumbering ',fn);
  539. {Read the message file}
  540. assign(f,fn);
  541. {$push} {$I-}
  542. reset(f);
  543. {$pop}
  544. if ioresult<>0 then
  545. begin
  546. WriteLn('*** message file '+fn+' not found ***');
  547. exit;
  548. end;
  549. assign(t,'msg2inc.$$$');
  550. rewrite(t);
  551. i:=0;
  552. while not eof(f) do
  553. begin
  554. readln(f,s);
  555. if (copy(s,1,length(Name))=Name) and (s[3] in ['0'..'9']) then
  556. begin
  557. inc(i);
  558. str(i,s1);
  559. while length(s1)<3 do
  560. s1:='0'+s1;
  561. writeln(t,Name+s1+Copy(s,6,255));
  562. end
  563. else
  564. writeln(t,s);
  565. end;
  566. close(t);
  567. close(f);
  568. { rename new file }
  569. erase(f);
  570. rename(t,fn);
  571. end;
  572. {*****************************************************************************
  573. WriteTexFile
  574. *****************************************************************************}
  575. Function EscapeString (Const S : String) : String;
  576. Var
  577. I : longint;
  578. hs : string;
  579. begin
  580. hs:='';
  581. for i:=1 to length(s) do
  582. case S[i] of
  583. '$' :
  584. if (s[i+1] in ['0'..'9']) then
  585. hs:=hs+'arg'
  586. else
  587. hs:=hs+'\$';
  588. '&','{','}','#','_','%': // Escape these characters
  589. hs := hs + '\' + S[i];
  590. '~','^':
  591. hs := hs + '\'+S[i]+' ';
  592. '\':
  593. hs:=hs+'$\backslash$'
  594. else
  595. hs := hs + S[i];
  596. end;
  597. EscapeString:=hs;
  598. end;
  599. procedure WriteTexFile(const infn,outfn:string);
  600. var
  601. t,f : text;
  602. line,
  603. i,k : longint;
  604. s,s1 : string;
  605. texoutput : boolean;
  606. begin
  607. Writeln('Loading messagefile ',infn);
  608. writeln('Writing TeXfile ',outfn);
  609. { Open infile }
  610. assign(f,infn);
  611. {$push} {$I-}
  612. reset(f);
  613. {$pop}
  614. if ioresult<>0 then
  615. begin
  616. WriteLn('*** message file '+infn+' not found ***');
  617. exit;
  618. end;
  619. { Open outfile }
  620. assign(t,outfn);
  621. rewrite(t);
  622. If texheader then
  623. begin
  624. writeln (t,'\documentclass{article}');
  625. writeln (t,'\usepackage{html}');
  626. writeln (t,'\usepackage{fpc}');
  627. writeln (t,'\begin{document}');
  628. end;
  629. { Parse }
  630. line:=0;
  631. TexOutput:=False;
  632. while not eof(f) do
  633. begin
  634. readln(f,s);
  635. inc(line);
  636. If Pos ('# BeginOfTeX',S)=1 then
  637. TexOutPut:=True
  638. else if pos ('# EndOfTeX',S)=1 then
  639. TexOutPut:=False;
  640. if (s<>'') and not(s[1] in ['#',';']) and TeXOutPut then
  641. begin
  642. if s[1]='%' then
  643. begin
  644. Delete(s,1,1);
  645. writeln(t,s);
  646. end
  647. else
  648. begin
  649. i:=pos('=',s);
  650. if i>0 then
  651. begin
  652. inc(i);
  653. while s[i] in ['0'..'9'] do
  654. inc(i);
  655. inc(i);
  656. s1:='';
  657. k:=0;
  658. while (k<5) and (s[i+k]<>'_') do
  659. begin
  660. case s[i+k] of
  661. 'W' : s1:='Warning: ';
  662. 'E' : s1:='Error: ';
  663. 'F' : s1:='Fatal: ';
  664. 'N' : s1:='Note: ';
  665. 'I' : s1:='Info: ';
  666. 'H' : s1:='Hint: ';
  667. end;
  668. inc(k);
  669. end;
  670. if s[i+k]='_' then
  671. inc(i,k+1);
  672. writeln(t,'\item ['+s1+escapestring(Copy(s,i,255))+']');
  673. end
  674. else
  675. writeln('error in line: ',line,' skipping');
  676. end;
  677. end;
  678. end;
  679. If TexHeader then
  680. writeln (t,'\end{document}');
  681. close(t);
  682. close(f);
  683. end;
  684. {*****************************************************************************
  685. Main Program
  686. *****************************************************************************}
  687. procedure getpara;
  688. var
  689. ch : char;
  690. para : string;
  691. files,i : word;
  692. procedure helpscreen;
  693. begin
  694. writeln('usage : msg2inc [Options] <msgfile> <incfile> <constname>');
  695. writeln('<Options> can be : -T Create .doc TeX file');
  696. writeln(' -TS Create .doc TeX file (stand-alone)');
  697. writeln(' -I Intel style asm output');
  698. writeln(' -S array of string');
  699. writeln(' -C array of char');
  700. writeln(' -R renumber section <incfile>');
  701. writeln(' -V Show version');
  702. writeln(' -? or -H This HelpScreen');
  703. halt(1);
  704. end;
  705. begin
  706. Mode:=M_String;
  707. FIles:=0;
  708. for i:=1to paramcount do
  709. begin
  710. para:=paramstr(i);
  711. if (para[1]='-') then
  712. begin
  713. ch:=upcase(para[2]);
  714. delete(para,1,2);
  715. case ch of
  716. 'T' : begin
  717. case upcase(para[1]) of
  718. 'S' : TexHeader:=True;
  719. end;
  720. Mode:=M_Tex;
  721. end;
  722. 'I' : Mode:=M_Intel;
  723. 'S' : Mode:=M_String;
  724. 'C' : Mode:=M_Char;
  725. 'R' : Mode:=M_Renumber;
  726. 'V' : begin
  727. Writeln('Msg2Inc ',version,' for Free Pascal (C) 1998-2002 Peter Vreman');
  728. Writeln;
  729. Halt;
  730. end;
  731. '?','H' : helpscreen;
  732. end;
  733. end
  734. else
  735. begin
  736. inc(Files);
  737. if Files>3 then
  738. HelpScreen;
  739. case Files of
  740. 1 : InFile:=Para;
  741. 2 : OutFile:=Para;
  742. 3 : OutName:=Para;
  743. end;
  744. end;
  745. end;
  746. case Mode of
  747. M_Renumber,
  748. M_Tex : if Files<2 then
  749. Helpscreen;
  750. else
  751. if FIles<3 then
  752. HelpScreen;
  753. end;
  754. end;
  755. begin
  756. GetPara;
  757. case Mode of
  758. M_Renumber : begin
  759. Renumberfile(Infile,OutFile);
  760. end;
  761. M_Tex : begin
  762. WriteTexFile(InFile,Outfile);
  763. end;
  764. M_Intel : begin
  765. Loadmsgfile(InFile);
  766. WriteEnumFile(OutFile+'idx.inc',OutName+'const');
  767. WriteIntelFile(OutFile+'txt.inc',OutName+'txt');
  768. end;
  769. M_String : begin
  770. Loadmsgfile(InFile);
  771. WriteEnumFile(OutFile+'idx.inc',OutName+'const');
  772. WriteStringFile(OutFile+'txt.inc',OutName+'txt');
  773. end;
  774. M_Char : begin
  775. Loadmsgfile(InFile);
  776. WriteEnumFile(OutFile+'idx.inc',OutName+'const');
  777. WriteCharFile(OutFile+'txt.inc',OutName+'txt');
  778. end;
  779. end;
  780. end.