<!--
   develbook.xml
-->

<chapter id="develbook">
   <title>
      Podręcznik developera ekg2
   </title>
   <sect1 id="develbook-programming">
      <title>Rozwijanie kodu ekg2</title>
      <sect2>
         <title>Wskazówki dla programistów</title>
         <itemizedlist>
            <listitem>
               <para>
                  Gdy coś poprawiasz/dodajesz, zachowuj styl reszty kodu.
                  Wcięcia rób znakami tabulacji, a nie spacjami. Stawiaj spacje
                  po przecinkach i średnikach. Jeśli masz inny styl, a
                  podesłane poprawki będą dobre i/lub potrzebne, nie zdziw się,
                  jeśli Twój kod zostanie przeregadowany.
               </para>
            </listitem>
            <listitem>
               <para>
                  Dopisuj się do copyrightów na początku pliku. Inaczej
                  zakładam, że zrzekasz się praw do podesłanych kawałków kodu.
                  Oczywiście podsyłany kod musi być na takiej samej licencji,
                  co reszta. W innym wypadku nie trać czasu i go nie przysyłaj.
               </para>
            </listitem>
            <listitem>
               <para>
                  Zachowuj przyjętą konwencję. Jeśli wszystkie zmienne danej
                  struktury mają nazwy typu <varname>foobar_count</varname>,
                  nie dodawaj <varname>foocount</varname>, czy
                  <varname>nfoo</varname>.
               </para>
            </listitem>
            <listitem>
               <para>
                  Nie zmieniaj api bez powodu. Nawet jeśli chcesz to zrobić,
                  skonsultuj z resztą developerów.
               </para>
            </listitem>
            <listitem>
               <para>
                  Tworząc większy kawałek/moduł/plik przeznaczony do jakiejś
                  konkretnej funkcji, staraj się nazywać funkcje i zmienne tak,
                  by było wiadomo, do czego służą. Przykład: funkcje dotyczące
                  list zaczynają się <function>list_*</function>, funkcje
                  dotyczące userlisty to <function>userlist_*</function>,
                  konfiguracja to <function>config_*</function>.
               </para>
            </listitem>
            <listitem>
               <para>
                  Do alokacji pamięci używaj funkcji
                  <function>xmalloc()</function>,
                  <function>xcalloc()</function>,
                  <function>xrealloc()</function> i
                  <function>xstrdup()</function>. Nie musisz sprawdzać
                  zwracanej wartości. Jeśli zabraknie pamięci, funkcje te same
                  zajmą się grzecznym zamknięciem programu.  Dbaj o zwalnianie
                  buforów, gdy nie są one już potrzebne.
               </para>
               <para>
                  Zamiast <function>strncpy()</function> oraz
                  <function>strncat()</function> używaj
                  <function>strlcpy()</function> i
                  <function>strlcat()</function>, które przyjmują jako parametr
                  całkowity rozmiar bufora. Nie trzeba się martwić o to, ile w
                  buforze pozostało jeszcze miejsca, czy znak zerowy się
                  zmieści, etc. Obie funkcje <emphasis>zawsze</emphasis>
                  zwracają ilość znaków, jaka została(by) zapisana do bufora.
               </para>
            </listitem>
            <listitem>
               <para>
                  Jeśli nie masz pojęcia o alokacji pamięci i obsłudze stringów
                  w C, najlepiej daj sobie spokój. Nawet jeśli kod działa, ale
                  nie trzyma się kupy, zostanie odrzucony.
               </para>
            </listitem>
            <listitem>
               <para>
                  Podsyłając patche, twórz je poleceniem <command>diff
                     -u</command>. diff bez parametrów generuje patche, które
                  nie zawierają żadnego kontekstu i ciężko je dołączyć do
                  źródła, gdy zmieniła się wcześniej chociaż jedna linijka.
                  Patche najlepiej generować względem najświeższej wersji, ale
                  nie jest to wymagane, jeśli w międzyczasie nie było poważnych
                  zmian w kodzie.
               </para>
            </listitem>
            <listitem>
               <para>
                  Jeśli poprawka jest niewielka (jedna, dwie linijki), nie trać
                  swojego, ani mojego czasu, tylko po prostu wklej oryginał i
                  poprawioną wersję do treści listu.
               </para>
            </listitem>
            <listitem>
               <para>
                  Poniższe linijki dopisane do ~/.vimrc ułatwiają wychwytywanie
                  znaków niedrukowalnych pozostawionych na końcach linii
                  poprzez zmianę ich koloru:
               </para>
               <programlisting>
<![CDATA[
    highlight WhitespaceEOL ctermbg=red guibg=red
    match WhitespaceEOL /\s\+$/
]]>
               </programlisting>
            </listitem>
            <listitem>
               <para>
                  Wszelkie komentarze powinny być w języku angielskim.
               </para>
            </listitem>
            <listitem>
               <para>
                  Wszystkie formatki i wszystko co zostaje wyświetlane na
                  ekranie ma być w języku angielskim.
               </para>
            </listitem>
            <listitem>
               <para>
                  Po zmianie jakiejkolwiek formatki należy w katalogu po/
                  wywołać make update-po i ewentualnie poprawić pliki *.po 
               </para>
            </listitem>
         </itemizedlist>
         <para>
	    Każdy może podsyłac patche, byle były zachowane pewne zasady i nie było
	    po miesiącu bałaganu w kodzie. Oczywiście wszystkie zmiany będą
	    przeglądane i w razie czego autor dostanie po łapach.
         </para>
      </sect2>
      <sect2>
         <title>API</title>
         <para>
            Dokumentacja API dostępna jest na stronie domowej projektu: <ulink
               url="http://www.ekg2.org/doxygen/">http://www.ekg2.org/doxygen/</ulink>
         </para>
      </sect2>
   </sect1>
   <sect1 id="develbook-files">
      <title>Formaty plików</title>
      <sect2>
         <title>Format listy kontaktów ekg2</title>
         <para>
            Lista kontaktów z pliku <filename>userlist</filename> jest tego samego
            formatu co eksportowana z windowsowego klienta:
         </para>
         <programlisting>
<![CDATA[
imię;nazwisko;pseudonim;wyświetlana_nazwa;telefon;grupy;opis;numer
]]>
         </programlisting>
         <para>
            ekg może dopisać użytkownika do kilku metagrup.
         </para>
         <itemizedlist>
            <listitem>
               <simpara>
                  __blocked - użytkownik blokowany
               </simpara>
            </listitem>
            <listitem>
               <simpara>
                  __offline - jesteśmy niedostępni dla użytkownika
               </simpara>
            </listitem>
            <listitem>
               <simpara>
                  __ignored_[poziom] - użytkownik jest ignorowany z danym poziomem
               </simpara>
            </listitem>
            <listitem>
               <simpara>
                  __ignored - wszystkie zdarzenia związane z użytkownikiem są ignorowane
               </simpara>
            </listitem>
         </itemizedlist>
      </sect2>
      <sect2>
         <title>Format konfiguracji ekg2</title>
         <para>
            Plik konfiguracyjny <filename>config</filename> zawiera kolejne
            wpisy w postaci:
         </para>
         <programlisting>
<![CDATA[
<set> <zmienna> <wartość>
bind <klawisz> <komenda>
bind-set <klawisz> <sekwencja>
alias <nazwa> <komenda>
on <zdarzenie> <priorytet> <założenia> <komenda>
at [nazwa] <czas>/<częstotliwość> <komenda>
timer [nazwa] [*/]<czas> <komenda>
plugin <nazwa>
]]>
         </programlisting>
         <para>
            Jeśli pierwszy wyraz nie jest jednym ze znanych, jest traktowany
            jako nazwa zmiennej, po której występuje jej wartość - chodzi o
            zachowanie kompatybilności z innymi klientami.
         </para>
         <para>
            Plik konfiguracyjny <filename>config-plugin</filename> zawiera
            kolejne wpisy w postaci:
         </para>
         <programlisting>
<![CDATA[
<zmienna> <wartość>
]]>
         </programlisting>
         <para>
            Plik konfiguracyjny ,,plugins'' zawiera kolejny wpisy w postaci:
         </para>
         <programlisting>
<![CDATA[
plugin <nazwa>
]]>
         </programlisting>
         <para>
            Jeśli wartość zmiennej zaczyna się od znaku o kodzie 1, to
            następująca po nim wartość zmiennej jest zakodowana używając
            base64.
         </para>
      </sect2>
      <sect2>
         <title>Format emotikon</title>
         <para>
            Za pomocą oryginalnego klienta GG można wysłać sekwencje znakowe,
            które będą się zamieniać na odpowiednie ikony. Z oczywistych
            względów w ekg wykonać się tego nie da (może ktoś kiedyś zrobi
            wsparcie dla aaliba ;))). Sekwencje te, zamieniane są więc na inne
            sekwencje. Np. sekwencja <literal>&lt;cmok&gt;</literal>może być
            zamieniona na <literal>:-*</literal> lub podobną, wedle uznania
            użytkownika.
         </para>
         <para>
            Aby skorzystać z możliwości rozwijania tego typu makr w
            otrzymywanych wiadomościach, należy je sobie wcześniej zdefiniować.
            Robi się to w pliku <filename>~/.gg/emoticons</filename>. Każda
            linia tego pliku definiuje jedno makro, a jej format jest
            Następujący:
         </para>
         <programlisting>
<![CDATA[
<makro><tabulatory><rozwiniecie>
]]>
         </programlisting>
         <para>
            Należy zwrócić uwagę na to, że do oddzielenia emoticonu od jego
            rozwinięcia służą tylko i wyłącznie tabulatory (jeden lub więcej).
            Spowodowane jest to występowaniem spacji w oryginalnych GG-owych
            emoticonach.
         </para>
         <para>
            Przykładowy zestaw definicji można znaleźć w docs/emoticons.sample.
            Nie zawiera on wszystkich definicji, bo nie miałem na tyle
            inwencji, żeby je powymyślać. 
         </para>
      </sect2>
      <sect2>
         <title>Format kolejki wiadomości</title>
         <para>
            Wiadomości są zapisywane w osobnych plikach w katalogu
            <filename>~/.gg/queue</filename>. Nazwa pliku jest połączeniem
            czasu wysłania wiadomości, kropki i kolejnej liczby, ale nie ma ona
            większego znaczenia. Chodzi o zachowanie kolejności przy sortowaniu
            plików. Pierwsza linia pliku zawiera wersję pliku. Aktualnie jest
            to ciąg znaków <literal>v1</literal>. Druga linia zawiera uid
            sesji, z której wiadomość była wysyłana. Trzecia linia zawiera
            listę odbiorców. Czwarta linia zawiera czas wysłania jako liczbę
            sekund od 1 stycznia 1970r. Piąta linia zawiera numer sekwencyjny
            lub dowolny ciąg znaków pozwalający jednoznacznie określić
            wiadomość. Kolejne linie są treścią wiadomości.
         </para>
      </sect2>
      <sect2>
         <title>Format pliku konfiguracji sesji ekg2</title>
         <para>
            Plik konfiguracyjny sessions-&lt;plugin&gt; może zawierać
            następujące definicje:
         </para>
         <programlisting>
<![CDATA[
	[uid sesji] - wszystkie zmienne będące poniżej są podrzędne dla 
		      tej sesji

	zmienna=wartość 
]]>
         </programlisting>
      </sect2>
   </sect1>
   <sect1 id="develbook-debugging">
      <title>Debugowanie</title>
      <para>
         Jeśli program się wywrócił i utworzył plik core, za pomocą gdb można
         sprawdzić, w którym miejscu wystąpił błąd. Najpierw uruchamiamy gdb.
      </para>
      <screen>
<![CDATA[
$ gdb ekg ~/.ekg2/core.<pid>
GNU gdb 5.0 (UI_OUT)
Copyright 2000 Free Software Foundation, Inc.
(...)
#0  command_test_segv (name=0x80617c9 "_segv", params=0x8088c20)
    at commands.c:1601
1601            return (*foo = 'A');
(gdb)
]]>
      </screen>
      <para>
         Od razu widzimy, że błąd wystąpił w funkcji
         <function>command_test_segv</function> z pliku
         <filename>commands.c</filename>. Potem widzimy błędną linię. W
         niektórych przypadkach niestety to nie wystarcza. Możliwe, że linia, w
         której wystąpił błąd jest poprawna, ale dostarczone dane nie były
         właściwe. Wtedy z pomocą przychodzi polecenie <command>bt</command>,
         które wyświetla stos wywołań funkcji.  Widzimy dzięki temu po kolei,
         jaka funkcja wywoływała jaką funkcję i z jakimi parametrami.
      </para>
      <screen>
<![CDATA[
(gdb) bt
#0  command_test_segv (name=0x80617c9 "_segv", params=0x8088c20)
    at commands.c:1601
#1  0x080506e2 in old_execute (target=0x0, line=0x0) at commands.c:2009
#2  0x08050980 in ekg_execute (target=0x0, line=0x806d700 "_segv")
    at commands.c:2136
#3  0x08059192 in ui_ncurses_loop () at ui-ncurses.c:231
#4  0x080578d8 in main (argc=1, argv=0xbffffb24) at ekg.c:726
#5  0x4008e38f in __libc_start_main () from /lib/libc.so.6
]]>
      </screen>
      <para>
         Teraz widzimy, że po prostu użytkownik wywołał komendę
         <command>_segv</command>.  co prawda widać to po samej nazwie funkcji,
         w której wystąpił błąd, ale w większości przypadków nie będzie to aż
         tak oczywiste.
      </para>
      <para>
         Niestety zdarza się i tak, że błędny kod narusza ważne obszary
         pamięci, a sam błąd występuje później, chociażby przy wywoływaniu
         funkcji alokacji pamięci. W takich przypadkach zlokalizowanie błędu
         jest znacznie trudniejsze.
      </para>
   </sect1>
   <sect1 id="develbook-themes">
      <title>Tworzenie tematów graficznych</title>
      <sect2>
         <title>Stworzenie nowego tematu</title>
         <para>
            Tworzenie własnego tematu graficznego do ekg2 najlepiej rozpocząc od
            wygenerowania "czystego" pliku z tematem, zawierającego ustawienia
            domyślne. Do wygenerowania takiego pliku można użyć prostego skryptu
            powłoki, na przykład takiego jak poniższy, który należy uruchomić w
            katalogu ze źródłami ekg2.
         </para>
         <programlisting role="shell">
<![CDATA[
grep -R -I 'format_add("' * | cut -d: -f2- | \
   perl -ne '/format_add\("(.*?)".+"(.*)".*/; print $1." ".$2."\n"' \
   > my.theme
   ]]>
         </programlisting>
         <para>
            Mając wygenerowany taki plik można przystąpić do modyfikacji.
            Wszystkie zmienione formatki należy zapisać, niezmienione można usunąć
            lub pozostawić. Następnie należy plik z tematem umieścić we właściwym
            katalogu - podkatalogu <filename>themes</filename> katalogu z danymi
            ekg2 (zazwyczaj <filename>/usr/share/ekg2</filename> lub
            <filename>/usr/local/share/ekg2</filename>) lub w katalogu z
            konfiguracją ekg2 (<filename>~/.ekg2</filename> lub
            <filename>~/etc/ekg2</filename>). Stworzony temat można przetestować
            ustawiając zmienną <varname>theme</varname>, nadając jej wartość taką
            jak nazwa pliku z tematem bez końcówki <literal>.theme</literal>.
         </para>
      </sect2>
      <sect2>
         <title>Formatki</title>
         <table frame="all">
            <title>Kolory w tematach graficznych</title>
            <tgroup cols="4">
               <colspec colname="kolor"/>
               <colspec colname="zwykly"/>
               <colspec colname="jasny"/>
               <colspec colname="tlo"/>
               <spanspec spanname="nocolor" namest="zwykly" nameend="tlo"/>
               <tbody>
                  <row>
                     <entry>
                        Kolor
                     </entry>
                     <entry>
                        Zwykły
                     </entry>
                     <entry>
                        Jasny
                     </entry>
                     <entry>
                        Tło
                     </entry>
                  </row>
                  <row>
                     <entry>
                        czarny/szary
                     </entry>
                     <entry>
                        %k
                     </entry>
                     <entry>
                        %K
                     </entry>
                     <entry>
                        %l
                     </entry>
                  </row>
                  <row>
                     <entry>
                        niebieski
                     </entry>
                     <entry>
                        %b
                     </entry>
                     <entry>
                        %B
                     </entry>
                     <entry>
                        %e
                     </entry>
                  </row>
                  <row>
                     <entry>
                        czerwony
                     </entry>
                     <entry>
                        %r
                     </entry>
                     <entry>
                        %R
                     </entry>
                     <entry>
                        %s
                     </entry>
                  </row>
                  <row>
                     <entry>
                        fioletowy
                     </entry>
                     <entry>
                        %m/%p
                     </entry>
                     <entry>
                        %M/%P
                     </entry>
                     <entry>
                        %q
                     </entry>
                  </row>
                  <row>
                     <entry>
                        turkusowy
                     </entry>
                     <entry>
                        %c
                     </entry>
                     <entry>
                        %C
                     </entry>
                     <entry>
                        %d
                     </entry>
                  </row>
                  <row>
                     <entry>
                        brązowy/żółty
                     </entry>
                     <entry>
                        %y
                     </entry>
                     <entry>
                        %Y
                     </entry>
                     <entry>
                        %z
                     </entry>
                  </row>
                  <row>
                     <entry>
                        zielony
                     </entry>
                     <entry>
                        %g
                     </entry>
                     <entry>
                        %G
                     </entry>
                     <entry>
                        %h
                     </entry>
                  </row>
                  <row>
                     <entry>
                        biały
                     </entry>
                     <entry>
                        %w
                     </entry>
                     <entry>
                        %W
                     </entry>
                     <entry>
                        %x
                     </entry>
                  </row>
                  <row>
                     <entry>
                        mrugający
                     </entry>
                     <entry>
                        %i
                     </entry>
                     <entry>
                        -
                     </entry>
                     <entry>
                        -
                     </entry>
                  </row>
                  <row>
                     <entry>
                        tłusty
                     </entry>
                     <entry>
                        %T
                     </entry>
                     <entry>
                        -
                     </entry>
                     <entry>
                        -
                     </entry>
                  </row>
                  <row>
                     <entry>
                        bez koloru
                     </entry>
                     <entry spanname="nocolor">
                        %n
                     </entry>
                  </row>
               </tbody>
            </tgroup>
         </table>
      </sect2>
      <sect2>
         <title>Dopełnianie do szerokości</title>
         <para>
            Przy parametrach %1-%9 można kazać dopełniać do konkretnej
            szerokości. przydaje się do wszelkiego rodzaju tabelek.
         </para>
         <table frame="all">
            <title>Dopełnianie do szerokości</title>
            <tgroup cols="2">
               <colspec name="typ"/>
               <colspec name="opis"/>
               <tbody>
                  <row>
                     <entry>
                        %[10]1
                     </entry>
                     <entry>
                        dopełnia spacjami z prawej pierwszy parametr do 10 pól
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %[-10]1
                     </entry>
                     <entry>
                        j.w. tylko że do lewej
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %[.5]1
                     </entry>
                     <entry>
                        dopełnia zerami
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %[,9]2
                     </entry>
                     <entry>
                        dopełnia kropkami
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %[_4]1
                     </entry>
                     <entry>
                        dopełnia znakami podkreślenia
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %(10)1
                     </entry>
                     <entry>
                        jeśli rozmiar parametru przekroczy 10 znaków nie obcina
                     </entry>
                  </row>
               </tbody>
            </tgroup>
         </table>
         <para>
            UWAGA! kolorkowe sekwencje ansi traktuje jak znaki, więc nie
            powinno się ich używać przy dopełnianiu parametrów.
         </para>
      </sect2>
      <sect2>
         <title>Końcówki zależne od płci</title>
         <para>
            Jeśli chce się rozróżniać przymiotniki dla różnych płci, można użyć
            %@n, gdzie ,,n'' to numer formatu, który bierzemy pod uwagę. jeśli
            ostatnią literą będzie ,,a'', %@n zostanie zastąpione przez ,,a'',
            w innym przypadku przez ,,y''. przykład: <literal>%> %1 jest
               dostępn%@1</literal>
         </para>
         <para>
            Należy wziąć uwagę, że w wielu wypadkach pseudonimy są najpierw
            formatowane przez known_user i unknown_user, więc trzeba podać
            osobny parametr z samym pseudonimem.
         </para>
      </sect2>
      <sect2>
         <title>Znaki zachęty</title>
         <table frame="all">
            <title>Znaki zachęty</title>
            <tgroup cols="2">
               <colspec name="typ"/>
               <colspec name="opis"/>
               <tbody>
                  <row>
                     <entry>
                        %>
                     </entry>
                     <entry>
                        prompt (domyślnie zielony)
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %!
                     </entry>
                     <entry>
                        error (domyślnie czerwony)
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %)
                     </entry>
                     <entry>
                        prompt2 (domyślnie turkusowy)
                     </entry>
                  </row>
                  <row>
                     <entry>
                        %#
                     </entry>
                     <entry>
                        timestamp (domyślnie GG:MM)
                     </entry>
                  </row>
               </tbody>
            </tgroup>
         </table>
         <para>
            <literal>%|</literal> oznacza koniec promptu. Jeśli ten format
            występuje, to przy przenoszeniu do następnej linii, tekst przed tym
            formatem zostanie wyświetlony ponownie.
         </para>
         <example>
            <title>Użycie formatu %|</title>
            <programlisting>
<![CDATA[
%> Długa linia, która zostanie podzielona na kilka linii
]]>
            </programlisting>
            <para>
               Na małym terminalu powyższy przykład wyświetli:
            </para>
            <screen>
<![CDATA[
	.-------------------------.
        | ::: Długa linia, która  |
        | zostanie podzielona na  |
        | kilka linii             |
	`-------------------------'
]]>
            </screen>
            <para>
               Po dodaniu %|, to znaczy:
            </para>
            <programlisting>
<![CDATA[
%> %|Długa linia, która zostanie podzielona na kilka linii
]]>
            </programlisting>
            <para>
               zostanie wyświetlone jako:
            </para>
            <screen>
<![CDATA[
        .-------------------------.
        | ::: Długa linia, która  |
	| ::: zostanie podzielona |
	| ::: na kilka linii      |
	`-------------------------'
]]>
            </screen>
         </example>
      </sect2>
      <sect2>
         <title>Użytkownicy spoza listy kontaktów</title>
         <para>
            Dwa specjalne formaty ,,known_user'' i ,,unknown_user'' określają,
            jak będą pokazywani userzy z listy i spoza listy kontaktów.
            Pierwszy za parametry przyjmuje %1 opis, %2 numerek, a drugi %1
            numerek.
         </para>
      </sect2>
      <sect2>
         <title>Prompty readline</title>
         <para>
            Wpisy readline_*, oprócz readline_prompt_query nie mogą zawierać
            żadnych ,,procentowych'' kodów sterujących. Podobnie jest z
            promptem config_changed.
         </para>
      </sect2>
      <sect2>
         <title>Marginesy</title>
         <para>
            (ncurses) W ekg2 można ustawiać tzw. marginesy poprzez zmienną
            ncurses:margin_size. Aby z nich korzystać należy w formatce użyć
            znaków /| do oddzielenia tekstu znajdującego się po prawej i po
            lewej stronie.
         </para>
         <example>
            <title>Przykład użycia marginesów</title>
            <programlisting>
<![CDATA[
tekst/|tekst2
]]>
            </programlisting>
            <para>
               Powyższa formatka zostanie wyświetlona jako:
            </para>
            <screen>
<![CDATA[
tekst        tekst2
]]>
            </screen>
            <para>
               Odstęp reguluje się poprzez zmienną ncurses:margin_size.
            </para>
         </example>
      </sect2>
   </sect1>
   <sect1 id="develbook-plugins">
      <title>Tworzenie pluginów</title>
      <sect2>
         <title>Struktura plugin_t</title>
      </sect2>
      <sect2>
         <title>Obsługa zdarzeń</title>
      </sect2>
      <sect2>
         <title>Wywoływanie poleceń ekg2</title>
      </sect2>
      <sect2>
         <title>Wyświetlanie danych</title>
      </sect2>
   </sect1>
</chapter>
<!--
   vim: filetype=docbk encoding=iso-8859-2
-->
