{"id":1256,"date":"2020-09-27T21:12:52","date_gmt":"2020-09-27T19:12:52","guid":{"rendered":"https:\/\/moijari.com\/?p=1256"},"modified":"2020-11-25T19:48:03","modified_gmt":"2020-11-25T17:48:03","slug":"uusi-kokeilu-db2-ja-dbs-0-03","status":"publish","type":"post","link":"https:\/\/moijari.com\/?p=1256","title":{"rendered":"Jatkoa tertulle DB2, DBS ja DBA 0.05"},"content":{"rendered":"\n<p>Kaikki oikeudet pid\u00e4tet\u00e4\u00e4n. Olen jo jonkin aikaa ihmetellyt satunnaisbittej\u00e4 ja terttu on j\u00e4\u00e4nyt v\u00e4hemm\u00e4lle huomiolle. Jos haluat lukea valmiimmista ohjelmista, t\u00e4ss\u00e4 fort (<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/moijari.com\/?p=964\">https:\/\/moijari.com\/?p=964<\/a>), t\u00e4ss\u00e4 ressu1.7 (<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/moijari.com\/?p=798\">https:\/\/moijari.com\/?p=798<\/a>), ja t\u00e4ss\u00e4 ressu1.8 (<a rel=\"noreferrer noopener\" target=\"_blank\" href=\"https:\/\/moijari.com\/?p=842\">https:\/\/moijari.com\/?p=842<\/a>). T\u00e4ss\u00e4 postissa esitell\u00e4\u00e4n uusi yritys tertusta. Ohjelma koostuu tietokannasta (DB2) ja html-lomakeohjelmasta (DBS). db_ alkuiset rutiinit kuuluvat tietokantaan ja dbs_alkuiset rutiinit html-liittym\u00e4\u00e4n. Postin aluksi k\u00e4yd\u00e4\u00e4n l\u00e4pi DB2 rutiinit ja sitten jatketaan DBS rutiineilla. T\u00e4ss\u00e4 postissa on listattuna kaikki rutiinit eli periaatteessa t\u00e4st\u00e4 voi copy-pastata toimivan version.<\/p>\n\n\n\n<p>Edit: Julkaisun j\u00e4lkeiset muutokset<\/p>\n\n\n\n<p>Lis\u00e4tty http-versio palvelinohjelmasta. Kirjoitettu server rutiinien (http_server ja https_server) htmlin taulukon luku yksinkertaisemmaksi (while htmlparams). Lis\u00e4ksi in-taulukon kokoa kasvatetaan dynaamisesti, jos on tarvetta, pituus oli ennen kovakoodattu. Jaettu sovellusta (DBA) ja palvelin osuutta (DBS) kahteen osaan.  Ongelmat ovat puuttuvien toiminnallisuuksien lis\u00e4ksi dba_app:in ja palvelimien pituudet koodina. Kummankin palvelimen yhteiset funktiokutsut voisi tehd\u00e4 aliohjelmiksi, varsinkin jos ja kun kutsuja tulee molemmista palvelimista. P\u00e4\u00e4ohjelma on tehty uudessa versiossa siten, ett\u00e4 komentokytkimill\u00e4 (&#8211;http, &#8211;https ja &#8211;port) valitaan k\u00e4ytett\u00e4v\u00e4 palvelin ja portin numero.<\/p>\n\n\n\n<p>Lyhennetty https_server ja http_server rutiineja siirt\u00e4m\u00e4ll\u00e4 niiden yhteiset osat funktioihin. Yhteiset osat ovat: getaddrinfo, socket, bind, listen ja close, ne ovat uudessa versiossa server_-alkuisissa funktioissa. Vastaavasti https_server ja http_server rutiinit kutsuvat n\u00e4it\u00e4 rutiineja.<\/p>\n\n\n\n<p>Kirjoitettu uudestaan html_printf rutiini nimell\u00e4 dbs_html_printf. Uudessa versiossa puskureita kasvatetaan tarvittaessa, edellisess\u00e4 versiossahan niiden pituudet olivat konstantteja. Dba_logon rutiinissa on esimerkkej\u00e4 uudesta kutsusta. My\u00f6s dbs_html_set ja dbs_html_clear rutiinit on muuttuneet.<\/p>\n\n\n\n<p>Palasteltu dba_app funktiota pienempiin kokonaisuuksiin. dba_app_value() funktioon on eroteltu varsinainen data, ja dba_app_memberid funktioon on eroteltu sarakkeen nimen tulostus. Lis\u00e4ksi lis\u00e4ttiin k\u00e4\u00e4nn\u00f6kset sarakkeen nimelle ja sovelluksen nimelle. Seuraavassa esimerkki ruotsinkielisist\u00e4 asiakas ohjelman k\u00e4\u00e4nn\u00f6ksist\u00e4:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'app' \"asiakas\", 'language' \"sv\", 'apptext' \"Kundhantering\"\n'memberid' \"asiakasnumero\", 'language' \"sv\", 'membertext' \"Kundnummer\"\n'memberid' \"asiakkaan nimi\", 'language' \"sv\", 'membertext' \"Kundens namn\"\n'memberid' \"asiakkaan osoite\", 'language' \"sv\", 'membertext' \"Kundens adress\"\n'memberid' \"asiakkaan postitoimipaikka\", 'language' \"sv\", 'membertext' \"Kundens postkontor\"\n'memberid' \"asiakkaan postiosoite\", 'language' \"sv\", 'membertext' \"Kundens postadress\"\n'memberid' \"asiakkaan maa\", 'language' \"sv\", 'membertext' \"Kundens land\"\n'memberid' \"asiakkaan email\", 'language' \"sv\", 'membertext' \"Kundens e-post\"<\/code><\/pre>\n\n\n\n<p>K\u00e4\u00e4nn\u00f6s asiakashallinnasta ven\u00e4j\u00e4ksi: (google translate)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'app' \"asiakas\", 'language' \"ru\", 'apptext' \"\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438\"\n'memberid' \"asiakasnumero\", 'language' \"ru\", 'membertext' \"\u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'memberid' \"asiakkaan nimi\", 'language' \"ru\", 'membertext' \"\u0418\u043c\u044f \u041a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'memberid' \"asiakkaan osoite\", 'language' \"ru\", 'membertext' \"\u0430\u0434\u0440\u0435\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'memberid' \"asiakkaan postitoimipaikka\", 'language' \"ru\", 'membertext' \"\u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0435 \u043e\u0442\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'memberid' \"asiakkaan postiosoite\", 'language' \"ru\", 'membertext' \"\u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0430\u0434\u0440\u0435\u0441 \u043f\u043e\u043a\u0443\u043f\u0430\u0442\u0435\u043b\u044f\"\n'memberid' \"asiakkaan maa\", 'language' \"ru\", 'membertext' \"\u0441\u0442\u0440\u0430\u043d\u0430 \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a\u0430\"\n'memberid' \"asiakkaan email\", 'language' \"ru\", 'membertext' \"\u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u0430\u044f \u043f\u043e\u0447\u0442\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"<\/code><\/pre>\n\n\n\n<p>Viel\u00e4 sama sovellus kiinaksi (google translate). Saan n\u00e4ist\u00e4 (ven\u00e4j\u00e4 ja kiina) paljon markkinointimateriaalia kommentteihin:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'app' \"asiakas\", 'language' \"ch\", 'apptext' \"\u7528\u6236\u7ba1\u7406\"\n'memberid' \"asiakasnumero\", 'language' \"ch\", 'membertext' \"\u9867\u5ba2\u865f\u78bc\"\n'memberid' \"asiakkaan nimi\", 'language' \"ch\", 'membertext' \"\u9867\u5ba2\u59d3\u540d\"\n'memberid' \"asiakkaan osoite\", 'language' \"ch\", 'membertext' \"\u5ba2\u6236\u5730\u5740\"\n'memberid' \"asiakkaan postitoimipaikka\", 'language' \"ch\", 'membertext' \"\u5ba2\u6236\u90f5\u5c40\"\n'memberid' \"asiakkaan postiosoite\", 'language' \"ch\", 'membertext' \"\u5ba2\u6236\u7684\u90f5\u653f\u5730\u5740\"\n'memberid' \"asiakkaan maa\", 'language' \"ch\", 'membertext' \"\u5ba2\u6236\u6240\u5728\u570b\u5bb6\"\n'memberid' \"asiakkaan email\", 'language' \"ch\", 'membertext' \"\u5ba2\u6236\u96fb\u5b50\u90f5\u4ef6\"<\/code><\/pre>\n\n\n\n<p>Jatkettu dba_app:in palastelua. T\u00e4ll\u00e4 kertaa siit\u00e4 on otettu foreign keys kappale, joka on nimetty dba_app_get_foreign_keys. Se antaa listan sovelluksen sarakkeista, joihin tarvitaan tieto mahdollisista sovelluksen ulkopuolisista kentist\u00e4.<\/p>\n\n\n\n<p>Edellisest\u00e4 p\u00e4ivityksest\u00e4 on sen verran aikaa, ett\u00e4 p\u00e4\u00e4tin postata t\u00e4m\u00e4n listan puolivalmisteena. T\u00e4ss\u00e4 on uusi tietosis\u00e4lt\u00f6, uudessa versiossa ovat asiakkaan tuotteen ja tilauksen lis\u00e4ksi toimitus ja laskutus m\u00e4\u00e4ritykset. dba_app_foreign_keys, dba_app_foreign_query, ja puuttumaan j\u00e4\u00e4 dba_app_foreign_data. Foreign keys p\u00e4\u00e4ttelee sovellusten v\u00e4liset vierasavaimet, foreign query muodostaa avaimista kyselyn ja foreign data (joka viel\u00e4 puuttuu) muodostaa merkkijonon tarvittavista kentist\u00e4 (jotka siis tulevat tuolta vierassovellukselta. Merkkijono luetaan app_value rutiinissa.<\/p>\n\n\n\n<p>Siirretty http_server ja https_server rutiineista yhteiset osat dba_main rutiiniin. Lis\u00e4tty automaattinen sovellusten tekemien kyselyjen ja niiden palauttaman datan tulostaminen html-sivun loppuun. Tuloste on tilaus n\u00e4yt\u00f6ll\u00e4 t\u00e4m\u00e4n n\u00e4k\u00f6inen: App tekee aika monta kysely\u00e4, mutta kysely suoritetaan vain jos sit\u00e4 ei ole aiemmin tehty. Eli sovelluskohtaiset kyselyt suoritetaan vain ensimm\u00e4isell\u00e4 kerralla ja seuraavalla kerralla palautetaan vain edellinen vastaus. T\u00e4t\u00e4 kyselyjen tulostusta varten on uudelleenpaketoitu db_query, db_query_first_data() ja db_data_next_data() uusiin dba_query ja dba_data alkuisiin rutiineihin.  samoin kysely\u00e4 varten on lis\u00e4tty uusi html_printf puskuri2, jonka koodia on my\u00f6s dba_main rutiinissa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>query: 'app' \"tilaus\", 'appname'\ndata: 'app' \"tilaus\", 'appname' \"Tilaukset\"\nquery: 'app' \"tilaus\", 'language' \"en\", 'apptext'\nquery: 'memberid'\ndata: 'memberid' \"asiakasnumero\"\ndata: 'memberid' \"asiakkaan email\"\ndata: 'memberid' \"asiakkaan maa\"\ndata: 'memberid' \"asiakkaan nimi\"\ndata: 'memberid' \"asiakkaan osoite\"\ndata: 'memberid' \"asiakkaan postinumero\"\ndata: 'memberid' \"asiakkaan postitoimipaikka\"\ndata: 'memberid' \"laskun asiakasnumero\"\ndata: 'memberid' \"laskun asiakkaan nimi\"\ndata: 'memberid' \"laskun rivinumero\"\ndata: 'memberid' \"laskun tilausnumero\"\ndata: 'memberid' \"laskun toimitusnumero\"\ndata: 'memberid' \"laskun tuotenumero\"\ndata: 'memberid' \"laskun tuotteen hinta\"\ndata: 'memberid' \"laskun tuotteen nimi\"\ndata: 'memberid' \"laskunumero\"\ndata: 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"tilauksen asiakasnumero\"\ndata: 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'memberid' \"tilauksen rivinumero\"\ndata: 'memberid' \"tilauksen tuotenumero\"\ndata: 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'memberid' \"tilausnumero\"\ndata: 'memberid' \"toimitettu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"toimittajan email\"\ndata: 'memberid' \"toimittajan maa\"\ndata: 'memberid' \"toimittajan nimi\"\ndata: 'memberid' \"toimittajan osoite\"\ndata: 'memberid' \"toimittajan postinumero\"\ndata: 'memberid' \"toimittajan postitoimipaikka\"\ndata: 'memberid' \"toimittajanumero\"\ndata: 'memberid' \"toimituksen asiakasnumero\"\ndata: 'memberid' \"toimituksen asiakkaan nimi\"\ndata: 'memberid' \"toimituksen rivinumero\"\ndata: 'memberid' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"toimituksen tilausnumero\"\ndata: 'memberid' \"toimituksen tuotenumero\"\ndata: 'memberid' \"toimituksen tuotteen hinta\"\ndata: 'memberid' \"toimituksen tuotteen nimi\"\ndata: 'memberid' \"toimitusnumero\"\ndata: 'memberid' \"tuotenumero\"\ndata: 'memberid' \"tuotteen hinta\"\ndata: 'memberid' \"tuotteen nimi\"\nquery: 'app', 'fromapp', 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"asiakas\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"asiakas2\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"tuote\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"tuote2\", 'toapp' \"tilaus\"\nquery: 'app' \"asiakas\", 'chapter', 'memberid'\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakasnumero\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan email\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan maa\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan nimi\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan osoite\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan postinumero\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan postitoimipaikka\"\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"asiakas2\", 'chapter', 'memberid'\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"tuote\", 'chapter', 'memberid'\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotenumero\"\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotteen hinta\"\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotteen nimi\"\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"tuote2\", 'chapter', 'memberid'\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'memberid'\ndata: 'memberid' \"asiakasnumero\"\ndata: 'memberid' \"asiakkaan email\"\ndata: 'memberid' \"asiakkaan maa\"\ndata: 'memberid' \"asiakkaan nimi\"\ndata: 'memberid' \"asiakkaan osoite\"\ndata: 'memberid' \"asiakkaan postinumero\"\ndata: 'memberid' \"asiakkaan postitoimipaikka\"\ndata: 'memberid' \"laskun asiakasnumero\"\ndata: 'memberid' \"laskun asiakkaan nimi\"\ndata: 'memberid' \"laskun rivinumero\"\ndata: 'memberid' \"laskun tilausnumero\"\ndata: 'memberid' \"laskun toimitusnumero\"\ndata: 'memberid' \"laskun tuotenumero\"\ndata: 'memberid' \"laskun tuotteen hinta\"\ndata: 'memberid' \"laskun tuotteen nimi\"\ndata: 'memberid' \"laskunumero\"\ndata: 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"tilauksen asiakasnumero\"\ndata: 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'memberid' \"tilauksen rivinumero\"\ndata: 'memberid' \"tilauksen tuotenumero\"\ndata: 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'memberid' \"tilausnumero\"\ndata: 'memberid' \"toimitettu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"toimittajan email\"\ndata: 'memberid' \"toimittajan maa\"\ndata: 'memberid' \"toimittajan nimi\"\ndata: 'memberid' \"toimittajan osoite\"\ndata: 'memberid' \"toimittajan postinumero\"\ndata: 'memberid' \"toimittajan postitoimipaikka\"\ndata: 'memberid' \"toimittajanumero\"\ndata: 'memberid' \"toimituksen asiakasnumero\"\ndata: 'memberid' \"toimituksen asiakkaan nimi\"\ndata: 'memberid' \"toimituksen rivinumero\"\ndata: 'memberid' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'memberid' \"toimituksen tilausnumero\"\ndata: 'memberid' \"toimituksen tuotenumero\"\ndata: 'memberid' \"toimituksen tuotteen hinta\"\ndata: 'memberid' \"toimituksen tuotteen nimi\"\ndata: 'memberid' \"toimitusnumero\"\ndata: 'memberid' \"tuotenumero\"\ndata: 'memberid' \"tuotteen hinta\"\ndata: 'memberid' \"tuotteen nimi\"\nquery: 'app', 'fromapp', 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"asiakas\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"asiakas2\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"tuote\", 'toapp' \"tilaus\"\ndata: 'app' \"tilaus\", 'fromapp' \"tuote2\", 'toapp' \"tilaus\"\nquery: 'app' \"asiakas\", 'chapter', 'memberid'\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakasnumero\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan email\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan maa\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan nimi\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan osoite\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan postinumero\"\ndata: 'app' \"asiakas\", 'chapter' \"header\", 'memberid' \"asiakkaan postitoimipaikka\"\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"asiakas2\", 'chapter', 'memberid'\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"tuote\", 'chapter', 'memberid'\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotenumero\"\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotteen hinta\"\ndata: 'app' \"tuote\", 'chapter' \"header\", 'memberid' \"tuotteen nimi\"\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"tuote2\", 'chapter', 'memberid'\nquery: 'app' \"tilaus\", 'chapter', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilauksen asiakkaan nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'memberid' \"tilausnumero\"\nquery: 'app' \"tilaus\", 'chapter'\ndata: 'app' \"tilaus\", 'chapter' \"header\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\"\nquery: 'app' \"tilaus\", 'chapter' \"header\", 'sort', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"tilauksen asiakasnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"tilauksen asiakkaan nimi\"\nquery: 'tilausnumero', 'tilauksen asiakasnumero', 'tilauksen asiakkaan nimi'\ndata: 'tilausnumero' \"3000\", 'tilauksen asiakasnumero' \"1000\", 'tilauksen asiakkaan nimi' \"Firma 1\"\nquery: 'asiakasnumero', 'asiakkaan nimi'\ndata: 'asiakasnumero' \"1000\", 'asiakkaan nimi' \"Firma 1\"\nquery: 'memberid' \"tilausnumero\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilausnumero\", 'language' \"en\", 'translation'\ndata: 'word' \"tilausnumero\", 'language' \"en\", 'translation' \"order product name\"\nquery: 'memberid' \"tilausnumero\", 'length'\nquery: 'memberid' \"tilausnumero\", 'mode'\nquery: 'memberid' \"tilauksen asiakasnumero\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen asiakasnumero\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen asiakasnumero\", 'language' \"en\", 'translation' \"order customer number\"\nquery: 'memberid' \"tilauksen asiakasnumero\", 'length'\nquery: 'memberid' \"tilauksen asiakasnumero\", 'mode'\nquery: 'memberid' \"tilauksen asiakkaan nimi\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen asiakkaan nimi\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen asiakkaan nimi\", 'language' \"en\", 'translation' \"order customer name\"\nquery: 'memberid' \"tilauksen asiakkaan nimi\", 'length'\ndata: 'memberid' \"tilauksen asiakkaan nimi\", 'length' \"32\"\nquery: 'memberid' \"tilauksen asiakkaan nimi\", 'mode'\nquery: 'app' \"tilaus\", 'chapter' \"lines\", 'sort', 'memberid'\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"04\", 'memberid' \"tilausnumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"05\", 'memberid' \"tilauksen rivinumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"06\", 'memberid' \"tilauksen tuotenumero\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"07\", 'memberid' \"tilauksen tuotteen nimi\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"08\", 'memberid' \"tilauksen tuotteen hinta\"\ndata: 'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"09\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\nquery: 'tilausnumero', 'tilauksen rivinumero', 'tilauksen tuotenumero', 'tilauksen tuotteen nimi', 'tilauksen tuotteen hinta', 'tilattu m\u00e4\u00e4r\u00e4'\ndata: 'tilausnumero' \"3000\", 'tilauksen rivinumero' \"10\", 'tilauksen tuotenumero' \"2000\", 'tilauksen tuotteen nimi' \"Tappi\", 'tilauksen tuotteen hinta' \"5,00\", 'tilattu m\u00e4\u00e4r\u00e4' \"1\"\nquery: 'memberid' \"tilausnumero\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilausnumero\", 'language' \"en\", 'translation'\ndata: 'word' \"tilausnumero\", 'language' \"en\", 'translation' \"order number\"\nquery: 'memberid' \"tilauksen rivinumero\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen rivinumero\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen rivinumero\", 'language' \"en\", 'translation' \"order line number\"\nquery: 'memberid' \"tilauksen tuotenumero\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen tuotenumero\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen tuotenumero\", 'language' \"en\", 'translation' \"order product number\"\nquery: 'memberid' \"tilauksen tuotteen nimi\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen tuotteen nimi\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen tuotteen nimi\", 'language' \"en\", 'translation' \"order product name\"\nquery: 'memberid' \"tilauksen tuotteen hinta\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilauksen tuotteen hinta\", 'language' \"en\", 'translation'\ndata: 'word' \"tilauksen tuotteen hinta\", 'language' \"en\", 'translation' \"order product price\"\nquery: 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'membertext'\nquery: 'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'translation'\ndata: 'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'translation' \"ordered quantity\"\nquery: 'tuotenumero', 'tuotteen hinta', 'tuotteen nimi'\ndata: 'tuotenumero' \"2000\", 'tuotteen hinta' \"5,00\", 'tuotteen nimi' \"Tappi\"\nquery: 'memberid' \"tilausnumero\", 'length'\nquery: 'memberid' \"tilausnumero\", 'mode'\nquery: 'memberid' \"tilauksen rivinumero\", 'length'\nquery: 'memberid' \"tilauksen rivinumero\", 'mode'\nquery: 'memberid' \"tilauksen tuotenumero\", 'length'\nquery: 'memberid' \"tilauksen tuotenumero\", 'mode'\nquery: 'memberid' \"tilauksen tuotteen nimi\", 'length'\ndata: 'memberid' \"tilauksen tuotteen nimi\", 'length' \"32\"\nquery: 'memberid' \"tilauksen tuotteen nimi\", 'mode'\nquery: 'memberid' \"tilauksen tuotteen hinta\", 'length'\nquery: 'memberid' \"tilauksen tuotteen hinta\", 'mode'\nquery: 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\", 'length'\nquery: 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\", 'mode' <\/code><\/pre>\n\n\n\n<p>Edellisess\u00e4 muutoksessa http_server() ja https_server() rutiinin per\u00e4kk\u00e4isten write:ien ja SSL_write:ien m\u00e4\u00e4r\u00e4 kasvoi nelj\u00e4\u00e4n. T\u00e4ss\u00e4 muutoksessa se pudotetaan taas yhteen, jota suoritetaan nelj\u00e4 kertaa &#8220;for(c=0&#8221;-luupissa. Lis\u00e4ksi muutama bugi yht\u00e4suuruus merkkej\u00e4 ja v\u00e4\u00e4ri\u00e4 avaimia korjattu tietosis\u00e4ll\u00f6ss\u00e4.<\/p>\n\n\n\n<p>Lis\u00e4tty uusi tapa tehd\u00e4 dynaamisia (eli automaattisesti kasvavia) merkkijonoja, funktiot dba_string_set ja dba_string_add. Funktioita k\u00e4ytet\u00e4\u00e4n uudessa dba_foreign_data() rutiinissa, vierasavain viittaukset ovat viel\u00e4kin puolivalmiste, viel\u00e4 tarvitaan app_value:n vierasavaimen hakeva osa. <\/p>\n\n\n\n<p>Lis\u00e4tty html merkkijonojen tulostukseen rutiini dbs_html_buf_printf(), jossa ensimm\u00e4iseksi parametriksi voi laittaa halutun puskurin numeron. Lis\u00e4tty html-lomakkeeseen foreign key kappale, jossa mainitaan lomakkeen vierasavaimet. Tilaus-lomakkeelle tulostuu seuraavat rivit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Foreign keys: 'tilauksen asiakasnumero' = \"asiakasnumero\", 'tilauksen asiakkaan nimi' = \"asiakkaan nimi\";; 'tilauksen tuotenumero' = \"tuotenumero\", 'tilauksen tuotteen hinta' = \"tuotteen hinta\", 'tilauksen tuotteen nimi' = \"tuotteen nimi\";;\nline 1 query: 'asiakasnumero' \"1000\", 'asiakkaan nimi';\nline 1 data: 'asiakasnumero' \"1000\", 'asiakkaan nimi' \"Firma 1\"\nline 2 query: 'tuotenumero' \"2000\", 'tuotteen hinta', 'tuotteen nimi';\nline 2 data: 'tuotenumero' \"2000\", 'tuotteen hinta' \"5,00\", 'tuotteen nimi' \"Tappi\"<\/code><\/pre>\n\n\n\n<p>Lis\u00e4tty sarakenimien (memberid) k\u00e4\u00e4nt\u00e4miseen ohjelma, jolla voi k\u00e4\u00e4nt\u00e4\u00e4 nimet halutuille kielille. Ohjelmaa k\u00e4ytet\u00e4\u00e4n kutsumalla dba_app_translations() rutiinia. Rutiinin kutsu on lis\u00e4tty dba_main funktioon. Rutiini k\u00e4ynnistet\u00e4\u00e4n &#8220;&#8211;translate&#8221; komentorivikytkimell\u00e4.<\/p>\n\n\n\n<p>Ensimm\u00e4isess\u00e4 luupissa dba_app_translations() tulostaa tertun kentist\u00e4 seuraavanlaisen listan: Lista koostuu tertun numeroiduista sarakenimist\u00e4. Numerolla viitataan seuraavassa vaiheessa t\u00e4m\u00e4n sanan k\u00e4\u00e4nn\u00f6kseen, jolla on tietysti sama numero.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>fi 0:asiakasnumero, 1:asiakkaan email, 2:asiakkaan maa, 3:asiakkaan nimi, 4:asiakkaan osoite, 5:asiakkaan postinumero, 6:asiakkaan postitoimipaikka, 7:laskun asiakasnumero, 8:laskun asiakkaan nimi, 9:laskun rivinumero, 10:laskun tilausnumero, 11:laskun toimitusnumero, 12:laskun tuotenumero, 13:laskun tuotteen hinta, 14:laskun tuotteen nimi, 15:laskunumero, 16:tilattu m\u00e4\u00e4r\u00e4, 17:tilauksen asiakasnumero, 18:tilauksen asiakkaan nimi, 19:tilauksen rivinumero, 20:tilauksen tuotenumero, 21:tilauksen tuotteen hinta, 22:tilauksen tuotteen nimi, 23:tilausnumero, 24:toimitettu m\u00e4\u00e4r\u00e4, 25:toimittajan email, 26:toimittajan maa, 27:toimittajan nimi, 28:toimittajan osoite, 29:toimittajan postinumero, 30:toimittajan postitoimipaikka, 31:toimittajanumero, 32:toimituksen asiakasnumero, 33:toimituksen asiakkaan nimi, 34:toimituksen rivinumero, 35:toimituksen tilattu m\u00e4\u00e4r\u00e4, 36:toimituksen tilausnumero, 37:toimituksen tuotenumero, 38:toimituksen tuotteen hinta, 39:toimituksen tuotteen nimi, 40:toimitusnumero, 41:tuotenumero, 42:tuotteen hinta, 43:tuotteen nimi<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 ensimm\u00e4inen rivi voidaan k\u00e4\u00e4nt\u00e4\u00e4 esimerkiksi googlella. jolloin se saadaan seuraavaan muotoon: T\u00e4m\u00e4 on siis yksi rivi, ja sen alkuun on lis\u00e4tty t\u00e4ss\u00e4 &#8216;en&#8217; joka viittaa kielen lyhenteeseem, t\u00e4ss\u00e4 englanti. Ensimm\u00e4inen rivi (t\u00e4ss\u00e4 edellinen fi alkuinen rivi) ja k\u00e4\u00e4nn\u00f6srivi lis\u00e4t\u00e4\u00e4n translations.dat tiedostoon, jolloin seuraava &#8211;translate ajo tekee (word, language, translation) rivit, jotka voidaan lis\u00e4t\u00e4 tietokantaan.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>en 0: customer number, 1: customer email, 2: customer country, 3: customer name, 4: customer address, 5: customer postal code, 6: customer post office, 7: invoice customer number, 8: invoice customer name, 9: invoice line number, 10: invoice order number, 11: invoice delivery number, 12: invoice product number, 13: invoice product price, 14: invoice product name, 15: invoice number, 16: ordered quantity, 17: order customer\n number, 18: order customer name, 19: order line number, 20: order product number, 21: order product price, 22: order product name, 23: order product name, 24: delivered quantity, 25: supplier email, 26: supplier country, 27: supplier name, 28: supplier address, 29: supplier's postal code, 30: supplier's post office, 31: supplier's number, 32: delivery's customer number, 33: delivery's customer's name, 34: delivery's line \nnumber, 35: delivery's ordered quantity, 36: delivery's order number, 37: delivery's product number, 38: delivery's product price, 39: delivery product name, 40: delivery number, 41: product number, 42: product price, 43: product name<\/code><\/pre>\n\n\n\n<p>T\u00e4ss\u00e4 alkua edellisten kahden rivin &#8211;translate tulosteesta: word niminen kentt\u00e4 tulee ensimm\u00e4isest\u00e4, fi-tietueesta nollan kohdalta (asiakasnumero), ja translation tulee en rivin 0-kohdasta (customer number). Asiakkaan email on sana 1 ja asiakkaan maa on sana 3 jne.<\/p>\n\n\n\n<p>translations.dat tiedoston ensimm\u00e4inen rivi on aina l\u00e4hde kieli, ja sen j\u00e4lkeen tulee yksi tai useampia kohdekieli\u00e4 omilla riveill\u00e4\u00e4n. Muista lis\u00e4t\u00e4 aina rivin alkuun kielikoodi. Kielikoodilla t\u00e4ytet\u00e4\u00e4n seuraavan raportin language kentt\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'word' \"asiakasnumero\", 'language' \"en\", 'translation' \"customer number\"\n'word' \"asiakkaan email\", 'language' \"en\", 'translation' \"customer email\"\n'word' \"asiakkaan maa\", 'language' \"en\", 'translation' \"customer country\"\n'word' \"asiakkaan nimi\", 'language' \"en\", 'translation' \"customer name\"\n'word' \"asiakkaan osoite\", 'language' \"en\", 'translation' \"customer address\"\n'word' \"asiakkaan postinumero\", 'language' \"en\", 'translation' \"customer postal code\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"en\", 'translation' \"customer post office\"\n'word' \"laskun asiakasnumero\", 'language' \"en\", 'translation' \"invoice customer number\"\n'word' \"laskun asiakkaan nimi\", 'language' \"en\", 'translation' \"invoice customer name\"<\/code><\/pre>\n\n\n\n<p>Varsinainen nimien k\u00e4\u00e4nn\u00f6s sovelluksessa tapahtuu dba_app_memberid() rutiinissa.<\/p>\n\n\n\n<p>T\u00e4m\u00e4 muutos  kuuluisi fort ohjelmaan, jota ei viel\u00e4 t\u00e4ss\u00e4 postissa ole ja  seuraavaan fort postiin on viel\u00e4 matkaa joten liit\u00e4n sen t\u00e4h\u00e4n.<\/p>\n\n\n\n<p>Ensimm\u00e4inen funktio fort_hash_http_page() lukee satunnaisuutta web sivulta hash:aamalla koko sivun sis\u00e4ll\u00f6n ja palauttamalla hash:in tuloksen muuttujassa hash. Ohjelmalle tarvitaan lis\u00e4ksi muuttujat host = &#8220;moijari.com&#8221;, port = &#8220;5001&#8221; ja page=&#8221;\/&#8221;, joka on ressulla satunnaisuusbittej\u00e4 tekev\u00e4 sivuni <a href=\"https:\/\/moijari.com:5001\">https:\/\/moijari.com:5001<\/a> (siis http:). En suosittele muuttamaan parametreja muiksi, eli hakemaan satunnaisuutta jonkun muun omistamasta sivusta. T\u00e4ss\u00e4 satunnaisuutta haetaan yhden kerran ohjelman ajon alussa.<\/p>\n\n\n\n<p>Toisessa osassa listaa on paikka jossa t\u00e4t\u00e4 rutiinia kutsutaan fort_init():iss\u00e4. Listassa on ensin jo aiemmin k\u00e4ytetty rutiini satunnaislukuja \/dev\/random:sta ja sen j\u00e4lkeen lis\u00e4t\u00e4\u00e4n t\u00e4m\u00e4 uusi FORT_USE_MOIJARICOM5001 rutiini. Seuraavassa alun definess\u00e4 t\u00e4m\u00e4 muutos on pois p\u00e4\u00e4lt\u00e4 (a).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define aFORT_USE_MOIJARICOM5001 2\n\n#ifdef FORT_USE_MOIJARICOM5001\n\n#include &lt;sys\/types.h>\n#include &lt;sys\/socket.h>\n#include &lt;netdb.h>\n#include &lt;sys\/types.h>\n#include &lt;sys\/socket.h>\n\nint fort_connect(unsigned char *host, unsigned char *port)\n{\n  struct addrinfo hints, *res, *resp;\n  int s, status;\n\n  memset(&amp;hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE;\n\n  if((status = getaddrinfo(host, port, &amp;hints, &amp;res)) != 0) {\n    fprintf(stderr,\"\\n%s: getaddrinfo\", procname);\n    fprintf(stderr,\", status %d\", status);\n    fprintf(stderr,\", gai_strerror(): %s\", gai_strerror(status));\n    fprintf(stderr,\", errno %d\\n\", errno);\n    fflush(stderr);\n  }\n\n  for(resp=res; resp!=NULL; resp = resp->ai_next) {\n    if((s = socket(resp->ai_family, resp->ai_socktype, resp->ai_protocol))&lt;0)\n      continue;\n    if(connect(s, resp->ai_addr, resp->ai_addrlen) == 0)\n      break;\n    close(s);\n  }\n\n  freeaddrinfo(res);\n\n  return(s);\n}\n\n#include &lt;stdarg.h>\n\nvoid dbs_printf(unsigned char **buf, int *buf_length, const char *format, ...)\n{\n  int count;\n  va_list args;\n\n  va_start(args, format);\n  count = snprintf(*buf, *buf_length, format, args) + 1;\n  va_end(args);\n  if(*buf_length &lt; count) {\n    *buf_length = count;\n    *buf = realloc(*buf, *buf_length);\n    va_start(args, format);\n    count = snprintf(*buf, *buf_length, format, args) + 1;\n    va_end(args);\n  }\n}\n\nvoid fort_hash_http_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash)\n{\n  int s,status,bytes,total;\n\n  if((s = fort_connect(host, port))&lt;0) {\n    fprintf(stderr,\"\\n%s: cannot fort_connect()\", procname);\n    fprintf(stderr,\", status: %d\", s);\n    fprintf(stderr,\", errno: %d\" , errno);\n    perror(\"fort_connect\");\n    fflush(stderr);\n  }\n\n  unsigned char *format =\n    \"GET %s HTTP\/1.0\\r\\n\"\n    \"Host: %s\\r\\n\";\n  static unsigned char *msg = NULL;\n  static int msg_length=0;\n\n  dbs_printf(&amp;msg, &amp;msg_length, format, page, host);\n\n  if((status=write(s, msg, strlen(msg)))&lt;0) {\n    fprintf(stderr, \"\\n%s: write(), error: %d\\n\", procname, errno);\n    perror(\"write\");\n    fflush(stderr);\n  }\n\n  HashCtx ctx;\n  char buffer&#91;1024];\n\n  HashInit(&amp;ctx);\n  total=0;\n  while((bytes = read(s, buffer, sizeof(buffer)))>0) {\n    \/\/write(1,buffer,bytes);                                                                                                                                                                                                                                                                                                                                                                                                                \n    HashUpdate(&amp;ctx, buffer, bytes);\n    total+=bytes;\n  }\n\n  HashFinal(hash, &amp;ctx);\n\n  fprintf(stdout,\"fort_hash_http_page: %d bytes read\", total);\n  fprintf(stdout,\", sha256: \");\n  for(int c = 0;c &lt; HashLen; c++) {\n    fprintf(stdout,\"%02x\", hash&#91;c]);\n  }\n  fprintf(stdout,\"\\n\");\n  fflush(stdout);\n\n  close(s);\n}\n\n#endif<\/code><\/pre>\n\n\n\n<p>Seuraavassa ovat siis fort_init():iin teht\u00e4v\u00e4t muutokset: eli FORT_USE_RANDOM kappaleen j\u00e4lkeen tuleva uusi FORT_USE_MOIJARICOM5001 kappale.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void fort_init()\n{\n  int c, pooltemp;\n\n  fprintf(stdout,\"Fort v0.49\");\n\n  . . .\n\n#ifdef FORT_USE_RANDOM\n  memset(temp, 0, sizeof(temp));\n  fort_readfile_xor(sizeof(temp), temp,\n      \"\/dev\/random\");\n  fort_reseed(sizeof(temp), temp);\n\n  dump_pools(\"Randomness from random\");\n#endif\n\n#ifdef FORT_USE_MOIJARICOM5001\n  unsigned char hash&#91;HashLen];\n\n  \/\/ do not change these parameters unless\n  \/\/ you own the target site.\n  fort_hash_http_page(\"moijari.com\", \"5001\", \"\/\", hash);\n  fort_reseed(sizeof(hash), hash);\n  dump_pools(\"Rand. from moijari.com:5001\");\n#endif\n\n  . . .\n}<\/code><\/pre>\n\n\n\n<p>Edellinen lis\u00e4\u00e4 fort:in alun satunnaisuuteen yhden l\u00e4hteen lis\u00e4\u00e4.<\/p>\n\n\n\n<p>Lis\u00e4tty https-versio satunnaislukusivun luvusta: sille minulla ei viel\u00e4 ole valmista luettavaa satunnaislukusivua, mutta sellainen tulee varmasti. Https funktiota ei ajeta viel\u00e4 fort_init():ss\u00e4, mutta lis\u00e4\u00e4n sen kun satunnaisbitit l\u00f6ytyv\u00e4t verkosta (moijari.com:5005). Lis\u00e4ksi palasteltu http ja https versioiden yhteiset osat fort_connect():iin ja dbs_printf():\u00e4\u00e4n.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#ifdef FORT_USE_MOIJARICOM5005\n\nvoid fort_hash_https_page(unsigned char *host,unsigned char *port, unsigned char *page, unsigned char *hash)\n{\n  int s, status, bytes, total;\n  struct addrinfo hints, *res, *resp;\n\n  SSL_METHOD *method=NULL;\n  SSL_CTX *ctx=NULL;\n  SSL *ssl;\n\n  SSL_library_init();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         \n  OpenSSL_add_ssl_algorithms();\n  SSL_load_error_strings();\n\n  if((method = (SSL_METHOD *)\n      SSLv23_client_method()) == NULL) {\n    fprintf(stderr,\"\\n%s: cannot SSLv23_client_method()\", procname);\n    fflush(stderr);\n  }\n\n  if((ctx=SSL_CTX_new(method)) == NULL) {\n    fprintf(stderr,\"\\n%s: cannot SSL_CTX_new()\", procname);\n    fflush(stderr);\n  }\n\n  if((ssl=SSL_new(ctx)) == NULL) {\n    fprintf(stderr,\"\\n%s: cannot SSL_new()\", procname);\n    fflush(stderr);\n  }\n\n  if((s = fort_connect(host, port))&lt;0) {\n    fprintf(stderr,\"\\n%s: cannot fort_connect()\", procname);\n    fprintf(stderr,\", status: %d\", s);\n    fprintf(stderr,\", errno: %d\" , errno);\n    perror(\"fort_connect\");\n    fflush(stderr);\n  }\n\n  SSL_set_fd(ssl,s);\n\n  if((status=SSL_connect(ssl))&lt;=0) {\n    fprintf(stderr,\"\\n%s: cannot SSL_connect()\", procname);\n    fprintf(stderr,\", status: %d\", status);\n    fprintf(stderr,\", errno: %d\" , errno);\n    fprintf(stderr,\", SSL_get_error(): %d\\n\", SSL_get_error(ssl,status));\n    perror(\"SSL_connect\");\n    fflush(stderr);\n  }\n\n  unsigned char *format =\n    \"GET %s HTTP\/1.0\\r\\n\"\n    \"Host: %s\\r\\n\";\n  static unsigned char *msg = NULL;\n  static int msg_length=0;\n\n  dbs_printf(&amp;msg,&amp;msg_length, format, page, host);\n\n  if((status=SSL_write(ssl, msg, strlen(msg)))&lt;0) {\n    fprintf(stderr,\"\\n%s: SSL_write()\", procname);\n    fprintf(stderr,\", status: %d\", status);\n    fprintf(stderr,\", errno: %d\", errno);\n    fprintf(stderr,\", SSL_get_error(): %d\", SSL_get_error(ssl,status));\n    perror(\"SSL_write\");\n    fflush(stderr);\n  }\n  fflush(stdout);\n\n  HashCtx hashctx;\n  char buffer&#91;1024];\n\n  HashInit(&amp;hashctx);\n  total=0;\n  while((bytes=SSL_read(ssl, buffer, sizeof(buffer)))>0) {\n    \/\/write(1,buffer,bytes);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      \n    HashUpdate(&amp;hashctx, buffer, bytes);\n    total+=bytes;\n  }\n  fflush(stdout);\n  if(bytes&lt;0) {\n    fprintf(stderr, \"\\n%s: SSL_read()\", procname);\n    fprintf(stderr, \", status: %d\", bytes);\n    fprintf(stderr, \", errno: %d\", errno);\n    fprintf(stderr,\", SSL_get_error(): %d\", SSL_get_error(ssl,status));\n    perror(\"SSL_read\");\n    fflush(stderr);\n  }\n\n  fflush(stdout);\n\n  HashFinal(hash, &amp;hashctx);\n\n  fprintf(stdout,\"dbs_hash_https_page: %d bytes read\", total);\n  fprintf(stdout,\", sha256: \");\n  for(int c = 0;c &lt; HashLen; c++) {\n    fprintf(stdout,\"%02x\", hash&#91;c]);\n  }\n  fprintf(stdout,\"\\n\");\n  fflush(stdout);\n\n  SSL_shutdown(ssl);\n  SSL_free(ssl);\n  SSL_CTX_free(ctx);\n  close(s);\n}\n\n#endif<\/code><\/pre>\n\n\n\n<p>Jos olet kiinnostjunut, seuraavassa tulosteessa osia html version lukemasta moijari.com:5001 sivusta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>HTTP\/1.0 200 OK\nLocation: \nServer: ressuhttps5 v0.96\nDate: Wed, 25 Nov 2020 15:41:59 GMT\nContent&amp;#8209;Length: 107442\n\n\n&lt;!DOCTYPE html>\n&lt;html lang=\"fi\">\n\t&lt;head>\n\t\t&lt;meta charset=\"UTF-8\">\n\t\t&lt;title>Ressu random numbers&lt;\/title>\n\t\t&lt;meta name=\"author\" content=\"Jari Kuivaniemi\">\n\t\t&lt;meta name=\"format-detection\" content=\"telephone=no\">\n\t&lt;\/head>\n\t&lt;body>\n\t\t&lt;h1>Ressu random numbers&lt;\/h1>\n\t\t&lt;code>\nJPQMWWlplNTnZD&amp;#8209;B U2GGaP_7TknMYUEv 9o8T9ERDp4jXPXRm aQpNmg6_ZP5l2BRy TbFBZ4I&amp;#8209;9bmRrLzH CTa&amp;#8209;LuF2sug_E0k\nJ QMuBmGq88Rj&amp;#8209;Vcoa pkdcXcvhXKQX&amp;#8209;1xS jSyPFA_3mtQQDLXZ &amp;#8209;TphlonN1qZDApye 2jqNVF9u&amp;#8209;CWU&amp;#8209;cCp lsDPdDoZ_fp8gHyj kncA36CcbhMK0PPU ug431N3LhHceFqXi iZI7tlVBB&amp;#8209;hHuk1C Y7Sg0rQlAp1zQGE\nE UVe7I6iukg8PlmI6 tbPU8giSZfYz2YMT mgpXrHLL7XBasYT5 436mqfv&amp;#8209;96ImgSNc\n\n...\n\n1_usGw7zVI_1ZAIY RnlkjWgphyTdawhF 7BPvno7f1oaAWovV Nda8PwrJfV&amp;#8209;jsUV2 x2oKjdMM7yydkzVS QidzMmXcneH1LG6A tH15FVbfE_BVR_Nn 0z48e1nliq2Wammg U7kkdBbLKkN9aonk efVA67Q3DoeSlG1W pBcssNPWh&amp;#8209;67343f\n\t\t&lt;code>\n\t\t&lt;h1>Statistics&lt;\/h1>\n\t\tmonobit: ones: 262454(50.062183%) zeroes: 261802(49.937817%), total: 524256&lt;br>\n\t\tbitwise monobit data: 1(0: 32793, 1: 32739) 2(0: 32885, 1: 32647) 4(0: 32698, 1: 32834) 8(0: 32844, 1: 32688) 16(0: 32829, 1: \n32703) 32(0: 32732, 1: 32800) 64(0: 32848, 1: 32684) 128(0: 32825, 1: 32707)&lt;br>\n\t\tbitwise monobit total: 524256, lowest: 32647(6.227301%), highest: 32885(6.272699%)&lt;br>\n\t\tpoker2: data:  0:65793 1:65493 2:65375 3:65467&lt;br>\n\t\tpoker2: total: 262128, lowest: 65375(24.940106%), highest: 65793(25.099570%)&lt;br>\n\t\tpoker4: data:  0:8252 1:8281 2:8145 3:8257 4:8167 5:8184 6:8306 7:8077 8:8257 9:7947 10:8186 11:8221 12:8182 13:8347 14:8127 1\n5:8128&lt;br>\n\t\tpoker4: total: 131064, lowest: 7947(6.063450%), highest: 8347(6.368644%)&lt;br>\n\t\tpoker8: data:  0:262 1:263 2:256 3:241 4:221 5:262 6:255 7:251 8:264 9:267 10:241 11:273 12:273 13:284 14:280 15:225 16:249 17\n\n...\n\naverage: total 8341993, count: 65532, average: 127.296478&lt;br>\n\t\tentropy: 7.997073&lt;br>\n\t\t&lt;h1>Program version (powered by Ressu 1.1)&lt;\/h1>\n\t\tprogram version: ressuhttps5 v0.96 sha256(e198d61ce5811a6d694775c4ecd3b1791f77a0e1ff71779c31c291c9f4017ba4)&lt;br>\n\t\t8 bit data sha256(a0440b769795c03c1a131c65c4e736fa07fad97a6ae10236a828208f8d8e5494)&lt;br>\n\t\t6 bit data sha256(947780866763677d5aecec7fe78268d3c8ff9e00a995e50c769738fd1b43d7d4)&lt;br>\n\t\toriginal url: &lt;a href=\"https:\/\/moijari.com:5001\">https:\/\/moijari.com:5001&lt;\/a>\n\t\t or &lt;a href=\"https:\/\/moijari.com:5001\">https:\/\/moijari.com:5001&lt;\/a>&lt;br>\n\t\t&lt;br>&lt;br>\n\t&lt;\/body>\n&lt;\/html><\/code><\/pre>\n\n\n\n<p>Edit: End of Julkaisun j\u00e4lkeiset muutokset<\/p>\n\n\n\n<p>Tietosis\u00e4lt\u00f6 on nyt seuraavankaltaisessa tiedostossa:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>;\n;\n;       Asiakas\n;\n;\n'app' \"asiakas\", 'appname' \"Asiakkaiden hallinta\"\n'memberid' \"asiakasnumero\", 'text', \"Asiakasnumero\" 'key' \"key\"\n'memberid' \"asiakkaan nimi\", 'text', \"Asiakkaan nimi\", 'length' \"32\"\n'memberid' \"asiakkaan osoite\", 'text', \"Asiakkaan osoite\", 'length' \"32\", 'mode' \"Optional\"\n'memberid' \"asiakkaan postinumero\", 'text', \"Asiakkaan postinumero\", 'length' \"10\", 'mode' \"Optional\"\n'memberid' \"asiakkaan postitoimipaikka\", 'text', \"Asiakkaan postitoimipaikka\", 'length' \"32\", 'mode' \"Optional\"\n'memberid' \"asiakkaan maa\", 'text', \"Asiakkaan maa\", 'length' \"32\", 'mode' \"Optional\"\n'memberid' \"asiakkaan email\", 'text', \"Asiakkaan email\", 'length' \"32\", 'mode' \"Required\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"asiakasnumero\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"asiakkaan nimi\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"asiakkaan osoite\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"04\", 'memberid' \"asiakkaan postinumero\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"05\", 'memberid' \"asiakkaan postitoimipaikka\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"06\", 'memberid' \"asiakkaan maa\"\n'app' \"asiakas\", 'chapter' \"header\", 'sort' \"07\", 'memberid' \"asiakkaan email\"\n'asiakasnumero' \"1000\", 'asiakkaan nimi' \"Firma 1\", 'asiakkaan osoite' \"Venekatu 1\", 'asiakkaan postinumero' \"00500\", 'asiakkaan postitoimipaikka' \"111111\", 'asiakkaan maa' \"Finland\", 'asiakkaan email' \"jarik@example.com\"\n'app' \"asiakas\", 'language' \"fi\", 'apptext' \"Asiakkaiden hallinta\"\n'memberid' \"asiakasnumero\", 'language' \"fi\", 'membertext' \"Asiakasnumero\"\n'memberid' \"asiakkaan nimi\", 'language' \"fi\", 'membertext' \"Asiakkaan nimi\"\n'memberid' \"asiakkaan osoite\", 'language' \"fi\", 'membertext' \"Asiakkaan osoite\"\n'memberid' \"asiakkaan postinumero\", 'language' \"fi\", 'membertext' \"Asiakkaan postinumero\"\n'memberid' \"asiakkaan postitoimipaikka\", 'language' \"fi\", 'membertext' \"Asiakkaan postitoimipaikka\"\n'memberid' \"asiakkaan maa\", 'language' \"fi\", 'membertext' \"Asiakkaan maa\"\n'memberid' \"asiakkaan email\", 'language' \"fi\", 'membertext' \"Asiakkaan s\u00e4hk\u00f6posti\"\n'app' \"asiakas\", 'language' \"sv\", 'apptext' \"Kundhantering\"\n'memberid' \"asiakasnumero\", 'language' \"sv\", 'membertext' \"Kundnummer\"\n'memberid' \"asiakkaan nimi\", 'language' \"sv\", 'membertext' \"Kundens namn\"\n'memberid' \"asiakkaan osoite\", 'language' \"sv\", 'membertext' \"Kundens adress\"\n'memberid' \"asiakkaan postinumero\", 'language' \"sv\", 'membertext' \"Kundens postnummer\"\n'memberid' \"asiakkaan postitoimipaikka\", 'language' \"sv\", 'membertext' \"Kundens postkontor\"\n'memberid' \"asiakkaan maa\", 'language' \"sv\", 'membertext' \"Kundens land\"\n'memberid' \"asiakkaan email\", 'language' \"sv\", 'membertext' \"Kundens e-post\"\n'app' \"asiakas\", 'language' \"ru\", 'apptext' \"\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438\"\n'app' \"asiakas\", 'language' \"ch\", 'apptext' \"\u7528\u6236\u7ba1\u7406\"\n;\n;\n;       Tuote\n;\n;\n'app' \"tuote\", 'appname' \"Tuotteet\"\n'memberid' \"tuotenumero\" 'key' \"key\"\n'memberid' \"tuotteen nimi\", 'text', \"Tuotteen nimi\", 'length' \"32\"\n'app' \"tuote\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"tuotenumero\"\n'app' \"tuote\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"tuotteen nimi\"\n'app' \"tuote\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"tuotteen hinta\"\n'tuotenumero' \"2000\", 'tuotteen nimi' \"Tappi\", 'tuotteen hinta' \"5,00\"\n;\n;\n;       Tilaus\n;\n;\n'app' \"tilaus\", 'appname' \"Tilaukset\"\n'app' \"tilaus\", 'fromapp' \"asiakas\", 'toapp' \"tilaus\"\n'app' \"tilaus\", 'fromapp' \"tuote\", 'toapp' \"tilaus\"\n'app' \"tilaus\", 'fromapp' \"asiakas2\", 'toapp' \"tilaus\"\n'app' \"tilaus\", 'fromapp' \"tuote2\", 'toapp' \"tilaus\"\n'memberid' \"tilausnumero\" 'key' \"key\"\n'memberid' \"tilauksen asiakkaan nimi\", 'text', \"Tilauksen asiakkaan nimi\", 'length' \"32\"\n'memberid' \"tilauksen rivinumero\" 'key' \"key\"\n'memberid' \"tilauksen tuotteen nimi\", 'text', \"Tilauksen tuotteen nimi\", 'length' \"32\"\n'app' \"tilaus\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"tilausnumero\"\n'app' \"tilaus\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"tilauksen asiakasnumero\"\n'app' \"tilaus\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"tilauksen asiakkaan nimi\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"04\", 'memberid' \"tilausnumero\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"05\", 'memberid' \"tilauksen rivinumero\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"06\", 'memberid' \"tilauksen tuotenumero\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"07\", 'memberid' \"tilauksen tuotteen nimi\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"08\", 'memberid' \"tilauksen tuotteen hinta\"\n'app' \"tilaus\", 'chapter' \"lines\", 'sort' \"09\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\n'tilausnumero' \"3000\", 'tilauksen asiakasnumero' \"1000\", 'tilauksen asiakkaan nimi' \"Firma 1\"\n'tilausnumero' \"3000\", 'tilauksen rivinumero' \"10\", 'tilauksen tuotenumero' \"2000\", 'tilauksen tuotteen nimi' \"Tappi\", 'tilauksen tuotteen hinta' \"5,00\", 'tilattu m\u00e4\u00e4r\u00e4' \"1\"\n'app' \"tilausrivit\", 'appname' \"Tilausrivit\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"tilausnumero\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"tilauksen asiakasnumero\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"tilauksen asiakkaan nimi\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"04\", 'memberid' \"tilauksen rivinumero\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"05\", 'memberid' \"tilauksen tuotenumero\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"06\", 'memberid' \"tilauksen tuotteen nimi\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"07\", 'memberid' \"tilauksen tuotteen hinta\"\n'app' \"tilausrivit\", 'chapter' \"header\", 'sort' \"08\", 'memberid' \"tilattu m\u00e4\u00e4r\u00e4\"\n;\n;\n;      Toimitus\n;\n;\n'app' \"toimitus\", 'appname' \"Toimitukset\"\n'app' \"toimitus\", 'fromapp' \"tilaus\", 'toapp' \"toimitus\"\n'app' \"toimitus\", 'fromapp' \"asiakas\", 'toapp' \"toimitus\"\n'app' \"toimitus\", 'fromapp' \"tuote\", 'toapp' \"toimitus\"\n'app' \"toimitus\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"toimitusnumero\"\n'app' \"toimitus\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"toimituksen asiakasnumero\"\n'app' \"toimitus\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"toimituksen asiakkaan nimi\"\n'app' \"toimitus\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"toimituksen tilausnumero\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"04\", 'memberid' \"toimitusnumero\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"05\", 'memberid' \"toimituksen rivinumero\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"06\", 'memberid' \"toimituksen tuotenumero\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"07\", 'memberid' \"toimituksen tuotteen nimi\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"08\", 'memberid' \"toimituksen tuotteen hinta\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"09\", 'memberid' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\"\n'app' \"toimitus\", 'chapter' \"lines\", 'sort' \"09\", 'memberid' \"toimitettu m\u00e4\u00e4r\u00e4\"\n'toimitusnumero' \"4000\", 'toimituksen asiakasnumero' \"1000\", 'toimituksen asiakkaan nimi' \"Firma 1\", 'toimituksen tilausnumero' \"3000\"\n'toimitusnumero' \"4000\", 'toimituksen rivinumero' \"10\", 'toimituksen tuotenumero' \"2000\", 'toimituksen tuotteen nimi' \"Tappi\", 'toimituksen tuotteen hinta' \"5,00\", 'toimituksen tilattu m\u00e4\u00e4r\u00e4' \"1\", 'toimitettu m\u00e4\u00e4r\u00e4' \"1\"\n;\n;\n;       Laskutus\n;\n\n'app' \"lasku\", 'appname' \"Laskut\"\n'app' \"lasku\", 'fromapp' \"toimitus\", 'toapp' \"lasku\"\n'app' \"lasku\", 'fromapp' \"asiakas\", 'toapp' \"lasku\"\n'app' \"lasku\", 'fromapp' \"tuote\", 'toapp' \"lasku\"\n'app' \"lasku\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"laskunumero\"\n'app' \"lasku\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"laskun asiakasnumero\"\n'app' \"lasku\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"laskun asiakkaan nimi\"\n'app' \"lasku\", 'chapter' \"header\", 'sort' \"04\", 'memberid' \"laskun tilausnumero\"\n'app' \"lasku\", 'chapter' \"header\", 'sort' \"05\", 'memberid' \"laskun toimitusnumero\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"10\", 'memberid' \"laskunumero\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"11\", 'memberid' \"laskun rivinumero\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"12\", 'memberid' \"laskun tuotenumero\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"13\", 'memberid' \"laskun tuotteen nimi\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"14\", 'memberid' \"laskun tuotteen hinta\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"15\", 'memberid' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\"\n'app' \"lasku\", 'chapter' \"lines\", 'sort' \"16\", 'memberid' \"toimitettu m\u00e4\u00e4r\u00e4\"\n'laskunumero' \"5000\", 'laskun asiakasnumero' \"1000\", 'laskun asiakkaan nimi' \"Firma 1\", 'laskun tilausnumero' \"3000\", 'laskun toimitusnumero' \"4000\"\n'laskunumero' \"5000\", 'laskun rivinumero' \"10\", 'laskun tuotenumero' \"2000\", 'laskun tuotteen nimi' \"Tappi\", 'laskun tuotteen hinta' \"5,00\", 'toimituksen tilattu m\u00e4\u00e4r\u00e4' \"1\", 'toimitettu m\u00e4\u00e4r\u00e4' \"1\"\n;\n;\n;       Toimittaja\n;\n;\n'app' \"toimittaja\", 'appname' \"Toimittajat\"\n'memberid' \"toimittajanumero\" 'key' \"key\"\n'memberid' \"toimittajan nimi\", 'text', \"Toimittajan nimi\", 'length' \"32\"\n'memberid' \"toimittajan osoite\", 'text', \"Toimittajan osoite\", 'length' \"32\"\n'memberid' \"toimittajan postinumero\", 'text', \"Toimittajan postinumero\", 'length' \"10\"\n'memberid' \"toimittajan postitoimipaikka\", 'text', \"Toimittajan postitoimipaikka\", 'length' \"32\"\n'memberid' \"toimittajan maa\", 'text', \"Toimittajan maa\", 'length' \"32\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"01\", 'memberid' \"toimittajanumero\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"02\", 'memberid' \"toimittajan nimi\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"03\", 'memberid' \"toimittajan osoite\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"04\", 'memberid' \"toimittajan postinumero\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"05\", 'memberid' \"toimittajan postitoimipaikka\"\n'app' \"toimittaja\", 'chapter' \"header\", 'sort' \"06\", 'memberid' \"toimittajan maa\"\n'toimittajanumero' \"2000\", 'toimittajan nimi' \"Firma 1\", 'toimittajan osoite' \"Venekatu 1\", 'toimittajan postinumero' \"00500\", 'toimittajan postitoimipaikka' \"Helsinki\", 'toimittajan maa' \"Finland\"\n'memberid' \"toimittajanumero\", 'language' \"fi\", 'membertext' \"Toimittajanumero\"\n'memberid' \"toimittajan nimi\", 'language' \"fi\", 'membertext' \"Toimittajan nimi\"\n'memberid' \"toimittajan osoite\", 'language' \"fi\", 'membertext' \"Toimittajan osoite\"\n'memberid' \"toimittajan postinumero\", 'language' \"fi\", 'membertext' \"Toimittajan postinumero\"\n'memberid' \"toimittajan postitoimipaikka\", 'language' \"fi\", 'membertext' \"Toimittajan postitoimipaikka\"\n'memberid' \"toimittajan maa\", 'language' \"fi\", 'membertext' \"Toimittajan maa\"\n'memberid' \"toimittajan email\", 'language' \"fi\", 'membertext' \"Toimittajan email\"\n;\n;\n;       Translations (google)\n;\n;\n'word' \"asiakasnumero\", 'language' \"en\", 'translation' \"customer number\"\n'word' \"asiakkaan email\", 'language' \"en\", 'translation' \"customer email\"\n'word' \"asiakkaan maa\", 'language' \"en\", 'translation' \"customer country\"\n'word' \"asiakkaan nimi\", 'language' \"en\", 'translation' \"customer name\"\n'word' \"asiakkaan osoite\", 'language' \"en\", 'translation' \"customer address\"\n'word' \"asiakkaan postinumero\", 'language' \"en\", 'translation' \"customer postal code\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"en\", 'translation' \"customer post office\"\n'word' \"laskun asiakasnumero\", 'language' \"en\", 'translation' \"invoice customer number\"\n'word' \"laskun asiakkaan nimi\", 'language' \"en\", 'translation' \"invoice customer name\"\n'word' \"laskun rivinumero\", 'language' \"en\", 'translation' \"invoice line number\"\n'word' \"laskun tilausnumero\", 'language' \"en\", 'translation' \"invoice order number\"\n'word' \"laskun toimitusnumero\", 'language' \"en\", 'translation' \"invoice delivery number\"\n'word' \"laskun tuotenumero\", 'language' \"en\", 'translation' \"invoice product number\"\n'word' \"laskun tuotteen hinta\", 'language' \"en\", 'translation' \"invoice product price\"\n'word' \"laskun tuotteen nimi\", 'language' \"en\", 'translation' \"invoice product name\"\n'word' \"laskunumero\", 'language' \"en\", 'translation' \"invoice number\"\n'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'translation' \"ordered quantity\"\n'word' \"tilauksen asiakasnumero\", 'language' \"en\", 'translation' \"order customer number\"\n'word' \"tilauksen asiakkaan nimi\", 'language' \"en\", 'translation' \"order customer name\"\n'word' \"tilauksen rivinumero\", 'language' \"en\", 'translation' \"order line number\"\n'word' \"tilauksen tuotenumero\", 'language' \"en\", 'translation' \"order product number\"\n'word' \"tilauksen tuotteen hinta\", 'language' \"en\", 'translation' \"order product price\"\n'word' \"tilauksen tuotteen nimi\", 'language' \"en\", 'translation' \"order product name\"\n'word' \"tilausnumero\", 'language' \"en\", 'translation' \"order product name\"\n'word' \"toimitettu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'translation' \"delivered quantity\"\n'word' \"toimittajan email\", 'language' \"en\", 'translation' \"supplier email\"\n'word' \"toimittajan maa\", 'language' \"en\", 'translation' \"supplier country\"\n'word' \"toimittajan nimi\", 'language' \"en\", 'translation' \"supplier name\"\n'word' \"toimittajan osoite\", 'language' \"en\", 'translation' \"supplier address\"\n'word' \"toimittajan postinumero\", 'language' \"en\", 'translation' \"supplier's postal code\"\n'word' \"toimittajan postitoimipaikka\", 'language' \"en\", 'translation' \"supplier's post office\"\n'word' \"toimittajanumero\", 'language' \"en\", 'translation' \"supplier's number\"\n'word' \"toimituksen asiakasnumero\", 'language' \"en\", 'translation' \"delivery's customer number\"\n'word' \"toimituksen asiakkaan nimi\", 'language' \"en\", 'translation' \"delivery's customer's name\"\n'word' \"toimituksen rivinumero\", 'language' \"en\", 'translation' \"delivery's line number\"\n'word' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\", 'language' \"en\", 'translation' \"delivery's ordered quantity\"\n'word' \"toimituksen tilausnumero\", 'language' \"en\", 'translation' \"delivery's order number\"\n'word' \"toimituksen tuotenumero\", 'language' \"en\", 'translation' \"delivery's product number\"\n'word' \"toimituksen tuotteen hinta\", 'language' \"en\", 'translation' \"delivery's product price\"\n'word' \"toimituksen tuotteen nimi\", 'language' \"en\", 'translation' \"delivery product name\"\n'word' \"toimitusnumero\", 'language' \"en\", 'translation' \"delivery number\"\n'word' \"tuotenumero\", 'language' \"en\", 'translation' \"product number\"\n'word' \"tuotteen hinta\", 'language' \"en\", 'translation' \"product price\"\n'word' \"tuotteen nimi\", 'language' \"en\", 'translation' \"product name\"\n'word' \"asiakasnumero\", 'language' \"sv\", 'translation' \"kundnummer\"\n'word' \"asiakkaan email\", 'language' \"sv\", 'translation' \"kundens e-post\"\n'word' \"asiakkaan maa\", 'language' \"sv\", 'translation' \"kundland\"\n'word' \"asiakkaan nimi\", 'language' \"sv\", 'translation' \"kundnamn\"\n'word' \"asiakkaan osoite\", 'language' \"sv\", 'translation' \"kundadress\"\n'word' \"asiakkaan postinumero\", 'language' \"sv\", 'translation' \"kundens postnummer\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"sv\", 'translation' \"kundpost\"\n'word' \"laskun asiakasnumero\", 'language' \"sv\", 'translation' \"fakturakundnummer\"\n'word' \"laskun asiakkaan nimi\", 'language' \"sv\", 'translation' \"fakturakundnamn\"\n'word' \"laskun rivinumero\", 'language' \"sv\", 'translation' \"fakturaradnummer\"\n'word' \"laskun tilausnumero\", 'language' \"sv\", 'translation' \"fakturanummer\"\n'word' \"laskun toimitusnumero\", 'language' \"sv\", 'translation' \"fakturans leveransnummer\"\n'word' \"laskun tuotenumero\", 'language' \"sv\", 'translation' \"fakturans produktnummer\"\n'word' \"laskun tuotteen hinta\", 'language' \"sv\", 'translation' \"fakturans produktpris\"\n'word' \"laskun tuotteen nimi\", 'language' \"sv\", 'translation' \"fakturans produktnamn\"\n'word' \"laskunumero\", 'language' \"sv\", 'translation' \"fakturanumret\"\n'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"sv\", 'translation' \"best\u00e4llt kvantitet\"\n'word' \"tilauksen asiakasnumero\", 'language' \"sv\", 'translation' \"best\u00e4ll kundens nummer\"\n'word' \"tilauksen asiakkaan nimi\", 'language' \"sv\", 'translation' \"best\u00e4ll kundens namn\"\n'word' \"tilauksen rivinumero\", 'language' \"sv\", 'translation' \"order radnummer\"\n'word' \"tilauksen tuotenumero\", 'language' \"sv\", 'translation' \"best\u00e4ll produktnummer\"\n'word' \"tilauksen tuotteen hinta\", 'language' \"sv\", 'translation' \"best\u00e4ll produktpris\"\n'word' \"tilauksen tuotteen nimi\", 'language' \"sv\", 'translation' \"best\u00e4ll produktnamn\"\n'word' \"tilausnumero\", 'language' \"sv\", 'translation' \"best\u00e4ll produktnamn\"\n'word' \"toimitettu m\u00e4\u00e4r\u00e4\", 'language' \"sv\", 'translation' \"levererad kvantitet\"\n'word' \"toimittajan email\", 'language' \"sv\", 'translation' \"leverant\u00f6rs-e-post\"\n'word' \"toimittajan maa\", 'language' \"sv\", 'translation' \"leverant\u00f6rsland\"\n'word' \"toimittajan nimi\", 'language' \"sv\", 'translation' \"leverant\u00f6rsnamn\"\n'word' \"toimittajan osoite\", 'language' \"sv\", 'translation' \"leverant\u00f6rsadress\"\n'word' \"toimittajan postinumero\", 'language' \"sv\", 'translation' \"leverant\u00f6rens postnummer\"\n'word' \"toimittajan postitoimipaikka\", 'language' \"sv\", 'translation' \"leverant\u00f6rens postkontor\"\n'word' \"toimittajanumero\", 'language' \"sv\", 'translation' \"leverant\u00f6rens nummer\"\n'word' \"toimituksen asiakasnumero\", 'language' \"sv\", 'translation' \"leveransens kundnummer\"\n'word' \"toimituksen asiakkaan nimi\", 'language' \"sv\", 'translation' \"leverans kundens namn\"\n'word' \"toimituksen rivinumero\", 'language' \"sv\", 'translation' \"leveransens radnummer\"\n'word' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\", 'language' \"sv\", 'translation' \"leveransens best\u00e4llda antal\"\n'word' \"toimituksen tilausnumero\", 'language' \"sv\", 'translation' \"leveransens best\u00e4llningsnummer\"\n'word' \"toimituksen tuotenumero\", 'language' \"sv\", 'translation' \"leveransens produktnummer\"\n'word' \"toimituksen tuotteen hinta\", 'language' \"sv\", 'translation' \"leveransens produktpris\"\n'word' \"toimituksen tuotteen nimi\", 'language' \"sv\", 'translation' \"leveransproduktnamn\"\n'word' \"toimitusnumero\", 'language' \"sv\", 'translation' \"leveransnummer\"\n'word' \"tuotenumero\", 'language' \"sv\", 'translation' \"produktnummer\"\n'word' \"tuotteen hinta\", 'language' \"sv\", 'translation' \"produktpris\"\n'word' \"tuotteen nimi\", 'language' \"sv\", 'translation' \"produktnamn\"\n'word' \"asiakasnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan email\", 'language' \"ru\", 'translation' \"\u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan maa\", 'language' \"ru\", 'translation' \"\u0441\u0442\u0440\u0430\u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan nimi\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan osoite\", 'language' \"ru\", 'translation' \"\u0430\u0434\u0440\u0435\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan postinumero\", 'language' \"ru\", 'translation' \"\u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"ru\", 'translation' \"\u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0435 \u043e\u0442\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\"\n'word' \"laskun asiakasnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun asiakkaan nimi\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun rivinumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun tilausnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0437\u0430\u043a\u0430\u0437\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun toimitusnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun tuotenumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun tuotteen hinta\", 'language' \"ru\", 'translation' \"\u0446\u0435\u043d\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskun tuotteen nimi\", 'language' \"ru\", 'translation' \"\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"laskunumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0441\u0447\u0435\u0442\u0430-\u0444\u0430\u043a\u0442\u0443\u0440\u044b\"\n'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"ru\", 'translation' \"\u0437\u0430\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\"\n'word' \"tilauksen asiakasnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"tilauksen asiakkaan nimi\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u0437\u0430\u043a\u0430\u0437\u0447\u0438\u043a\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"tilauksen rivinumero\", 'language' \"ru\", 'translation' \"\u0437\u0430\u043a\u0430\u0437 \u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438\"\n'word' \"tilauksen tuotenumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"tilauksen tuotteen hinta\", 'language' \"ru\", 'translation' \"\u0446\u0435\u043d\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"tilauksen tuotteen nimi\", 'language' \"ru\", 'translation' \"\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"tilausnumero\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0437\u0430\u043a\u0430\u0437\u0430\"\n'word' \"toimitettu m\u00e4\u00e4r\u00e4\", 'language' \"ru\", 'translation' \"\u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimittajan email\", 'language' \"ru\", 'translation' \"\u0430\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajan maa\", 'language' \"ru\", 'translation' \"\u0441\u0442\u0440\u0430\u043d\u0430 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajan nimi\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajan osoite\", 'language' \"ru\", 'translation' \"\u0430\u0434\u0440\u0435\u0441 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajan postinumero\", 'language' \"ru\", 'translation' \"\u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajan postitoimipaikka\", 'language' \"ru\", 'translation' \"\u043f\u043e\u0447\u0442\u043e\u0432\u043e\u0435 \u043e\u0442\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimittajanumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0441\u0442\u0430\u0432\u0449\u0438\u043a\u0430\"\n'word' \"toimituksen asiakasnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen asiakkaan nimi\", 'language' \"ru\", 'translation' \"\u0438\u043c\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen rivinumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\", 'language' \"ru\", 'translation' \"\u0437\u0430\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen tilausnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u0437\u0430\u043a\u0430\u0437\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen tuotenumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen tuotteen hinta\", 'language' \"ru\", 'translation' \"\u0446\u0435\u043d\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430 \u0434\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimituksen tuotteen nimi\", 'language' \"ru\", 'translation' \"\u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"toimitusnumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0441\u0442\u0430\u0432\u043a\u0438\"\n'word' \"tuotenumero\", 'language' \"ru\", 'translation' \"\u043d\u043e\u043c\u0435\u0440 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430\"\n'word' \"tuotteen hinta\", 'language' \"ru\", 'translation' \"\u0446\u0435\u043d\u0430 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430\"\n'word' \"tuotteen nimi\", 'language' \"ru\", 'translation' \"\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0430\"\n'word' \"asiakasnumero\", 'language' \"sp\", 'translation' \"n\u00famero de cliente\"\n'word' \"asiakkaan email\", 'language' \"sp\", 'translation' \"correo electr\u00f3nico del cliente\"\n'word' \"asiakkaan maa\", 'language' \"sp\", 'translation' \"pa\u00eds del cliente\"\n'word' \"asiakkaan nimi\", 'language' \"sp\", 'translation' \"nombre del cliente\"\n'word' \"asiakkaan osoite\", 'language' \"sp\", 'translation' \"direcci\u00f3n del cliente\"\n'word' \"asiakkaan postinumero\", 'language' \"sp\", 'translation' \"c\u00f3digo postal del cliente\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"sp\", 'translation' \"oficina postal del cliente\"\n'word' \"laskun asiakasnumero\", 'language' \"sp\", 'translation' \"n\u00famero de cliente de la factura\"\n'word' \"laskun asiakkaan nimi\", 'language' \"sp\", 'translation' \"nombre del cliente de la factura\"\n'word' \"laskun rivinumero\", 'language' \"sp\", 'translation' \"n\u00famero de l\u00ednea de la factura\"\n'word' \"laskun tilausnumero\", 'language' \"sp\", 'translation' \"n\u00famero de pedido de la factura\"\n'word' \"laskun toimitusnumero\", 'language' \"sp\", 'translation' \"n\u00famero de entrega de la factura\"\n'word' \"laskun tuotenumero\", 'language' \"sp\", 'translation' \"n\u00famero de producto de la factura\"\n'word' \"laskun tuotteen hinta\", 'language' \"sp\", 'translation' \"precio del producto de la factura\"\n'word' \"laskun tuotteen nimi\", 'language' \"sp\", 'translation' \"nombre del producto de la factura\"\n'word' \"laskunumero\", 'language' \"sp\", 'translation' \"n\u00famero de factura\"\n'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"sp\", 'translation' \"cantidad pedida\"\n'word' \"tilauksen asiakasnumero\", 'language' \"sp\", 'translation' \"n\u00famero de cliente del pedido\"\n'word' \"tilauksen asiakkaan nimi\", 'language' \"sp\", 'translation' \"nombre del cliente del pedido\"\n'word' \"tilauksen rivinumero\", 'language' \"sp\", 'translation' \"pedido n\u00famero de l\u00ednea\"\n'word' \"tilauksen tuotenumero\", 'language' \"sp\", 'translation' \"n\u00famero de producto del pedido\"\n'word' \"tilauksen tuotteen hinta\", 'language' \"sp\", 'translation' \"precio del producto del pedido\"\n'word' \"tilauksen tuotteen nimi\", 'language' \"sp\", 'translation' \"nombre del producto del pedido\"\n'word' \"tilausnumero\", 'language' \"sp\", 'translation' \"nombre del producto del pedido\"\n'word' \"toimitettu m\u00e4\u00e4r\u00e4\", 'language' \"sp\", 'translation' \"cantidad entregada\"\n'word' \"toimittajan email\", 'language' \"sp\", 'translation' \"correo electr\u00f3nico del proveedor\"\n'word' \"toimittajan maa\", 'language' \"sp\", 'translation' \"pa\u00eds del proveedor\"\n'word' \"toimittajan nimi\", 'language' \"sp\", 'translation' \"nombre del proveedor\"\n'word' \"toimittajan osoite\", 'language' \"sp\", 'translation' \"direcci\u00f3n del proveedor\"\n'word' \"toimittajan postinumero\", 'language' \"sp\", 'translation' \"c\u00f3digo postal del proveedor\"\n'word' \"toimittajan postitoimipaikka\", 'language' \"sp\", 'translation' \"oficina postal del proveedor\"\n'word' \"toimittajanumero\", 'language' \"sp\", 'translation' \"n\u00famero del proveedor\"\n'word' \"toimituksen asiakasnumero\", 'language' \"sp\", 'translation' \"n\u00famero de cliente de la entrega\"\n'word' \"toimituksen asiakkaan nimi\", 'language' \"sp\", 'translation' \"nombre del cliente de la entrega\"\n'word' \"toimituksen rivinumero\", 'language' \"sp\", 'translation' \"n\u00famero de l\u00ednea de entrega\"\n'word' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\", 'language' \"sp\", 'translation' \"cantidad pedida de la entrega\"\n'word' \"toimituksen tilausnumero\", 'language' \"sp\", 'translation' \"n\u00famero de orden de la entrega\"\n'word' \"toimituksen tuotenumero\", 'language' \"sp\", 'translation' \"n\u00famero de producto de la entrega\"\n'word' \"toimituksen tuotteen hinta\", 'language' \"sp\", 'translation' \"precio del producto de la entrega\"\n'word' \"toimituksen tuotteen nimi\", 'language' \"sp\", 'translation' \"nombre del producto de entrega\"\n'word' \"toimitusnumero\", 'language' \"sp\", 'translation' \"n\u00famero de entrega\"\n'word' \"tuotenumero\", 'language' \"sp\", 'translation' \"n\u00famero de producto\"\n'word' \"tuotteen hinta\", 'language' \"sp\", 'translation' \"precio del producto\"\n'word' \"tuotteen nimi\", 'language' \"sp\", 'translation' \"nombre del producto\"\n'word' \"asiakasnumero\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u7f16\u53f7\"\n'word' \"asiakkaan email\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u7535\u5b50\u90ae\u4ef6\"\n'word' \"asiakkaan maa\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u56fd\u5bb6\/\u5730\u533a\"\n'word' \"asiakkaan nimi\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u540d\u79f0\"\n'word' \"asiakkaan osoite\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u5730\u5740\"\n'word' \"asiakkaan postinumero\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u90ae\u653f\u7f16\u7801\"\n'word' \"asiakkaan postitoimipaikka\", 'language' \"ch\", 'translation' \"\u5ba2\u6237\u90ae\u5c40\"\n'word' \"laskun asiakasnumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u5ba2\u6237\u7f16\u53f7\"\n'word' \"laskun asiakkaan nimi\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u5ba2\u6237\u540d\u79f0\"\n'word' \"laskun rivinumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u884c\u7f16\u53f7\"\n'word' \"laskun tilausnumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u8ba2\u5355\u53f7\"\n'word' \"laskun toimitusnumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u4ea4\u8d27\u53f7\"\n'word' \"laskun tuotenumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u4ea7\u54c1\u53f7\"\n'word' \"laskun tuotteen hinta\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u4ea7\u54c1\u4ef7\u683c\"\n'word' \"laskun tuotteen nimi\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u4ea7\u54c1\u540d\u79f0\"\n'word' \"laskunumero\", 'language' \"ch\", 'translation' \"\u53d1\u7968\u7f16\u53f7\"\n'word' \"tilattu m\u00e4\u00e4r\u00e4\", 'language' \"ch\", 'translation' \"\u8ba2\u8d2d\u6570\u91cf\"\n'word' \"tilauksen asiakasnumero\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u5ba2\u6237\u7f16\u53f7\"\n'word' \"tilauksen asiakkaan nimi\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u5ba2\u6237\u540d\u79f0\"\n'word' \"tilauksen rivinumero\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u884c\u53f7\"\n'word' \"tilauksen tuotenumero\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u4ea7\u54c1\u7f16\u53f7\"\n'word' \"tilauksen tuotteen hinta\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u4ea7\u54c1\u4ef7\u683c\"\n'word' \"tilauksen tuotteen nimi\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u4ea7\u54c1\u540d\u79f0\"\n'word' \"tilausnumero\", 'language' \"ch\", 'translation' \"\u8ba2\u5355\u4ea7\u54c1\u540d\u79f0\"\n'word' \"toimitettu m\u00e4\u00e4r\u00e4\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u6570\u91cf\"\n'word' \"toimittajan email\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u7535\u5b50\u90ae\u4ef6\"\n'word' \"toimittajan maa\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u56fd\u5bb6\/\u5730\u533a\"\n'word' \"toimittajan nimi\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u540d\u79f0\"\n'word' \"toimittajan osoite\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u5730\u5740\"\n'word' \"toimittajan postinumero\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u90ae\u653f\u7f16\u7801\"\n'word' \"toimittajan postitoimipaikka\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u90ae\u5c40\"\n'word' \"toimittajanumero\", 'language' \"ch\", 'translation' \"\u4f9b\u5e94\u5546\u7f16\u53f7\"\n'word' \"toimituksen asiakasnumero\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u5ba2\u6237\u7f16\u53f7\"\n'word' \"toimituksen asiakkaan nimi\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u5ba2\u6237\u7f16\u53f7\"\n'word' \"toimituksen rivinumero\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u884c\u7f16\u53f7\"\n'word' \"toimituksen tilattu m\u00e4\u00e4r\u00e4\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u8ba2\u5355\u6570\u91cf\"\n'word' \"toimituksen tilausnumero\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u8ba2\u5355\u7f16\u53f7\"\n'word' \"toimituksen tuotenumero\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u4ea7\u54c1\u7f16\u53f7\"\n'word' \"toimituksen tuotteen hinta\", 'language' \"ch\", 'translation' \"\u4ea4\u4ed8\u4ea7\u54c1\u4ef7\u683c\"\n'word' \"toimituksen tuotteen nimi\", 'language' \"ch\", 'translation' \"\u4ea4\u8d27\u4ea7\u54c1\u540d\u79f0\"\n'word' \"toimitusnumero\", 'language' \"ch\", 'translation' \"\u4ea4\u8d27\u7f16\u53f7\"\n'word' \"tuotenumero\", 'language' \"ch\", 'translation' \"\u4ea7\u54c1\u7f16\u53f7\"\n'word' \"tuotteen hinta\", 'language' \"ch\", 'translation' \"\u4ea7\u54c1\u4ef7\u683c\"\n'word' \"tuotteen nimi\", 'language' \"ch\", 'translation' \"\u4ea7\u54c1\u540d\u79f0\"<\/code><\/pre>\n\n\n\n<p>Db2:sen muuttujia:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct query *first_query=NULL;\n\n#define MAXNAME 33\n#define MAXVALUE 1024<\/code><\/pre>\n\n\n\n<p>Query rakenteen query osoite osoittaa kyselyyn ja data rakenteen data osoite osoittaa vastausriviin.<\/p>\n\n\n\n<p>DB2 puolen p\u00e4\u00e4funktiot:<\/p>\n\n\n\n<p>void db_open(char *filename)  &#8212; t\u00e4ll\u00e4 avataan tietokanta<\/p>\n\n\n\n<p>void db_query(char *query) &#8212; T\u00e4ll\u00e4 m\u00e4\u00e4ritell\u00e4\u00e4n kysely ja haetaan vastaus<\/p>\n\n\n\n<p>struct data *db_query_get_first(struct query *q) &#8212; T\u00e4ll\u00e4 saadaan kyselyn ensimm\u00e4inen vastausrivi.<\/p>\n\n\n\n<p>struct data *db_data_get_next(struct data *d) &#8212; T\u00e4ll\u00e4 saadaan kyselyn seuraava vastausrivi<\/p>\n\n\n\n<p>int db_data_get_field(struct data *d, unsigned char *name2, int value2len, unsigned char *value2) &#8212; luetaan kentt\u00e4 tietueesta<\/p>\n\n\n\n<p>void db_save(struct data *d) &#8212; Talletetaan tietue<\/p>\n\n\n\n<p>Db2 funktiot:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static void db_skipwhite(unsigned char **p2);\nstatic unsigned char *save_string(unsigned char *string);\nstatic struct data *db_create_data(unsigned char *data);\nstatic struct query *db_create_query(unsigned char *query);\nstatic struct query *db_fetch_query(char *query);\nvoid dump_queries();\nstatic struct data *db_add_data(struct data **data, unsigned char *buf);\nstatic struct data *db_query_add_data(struct query *q,unsigned char *buf);\nstatic int db_parsename(int namelen, unsigned char *name, unsigned char **p2);\nstatic int db_parsevalue(int valuelen, unsigned char *value, unsigned char **p2);\nstatic int db_parse_nameandvalue(int namelen, unsigned char *name, int valuelen, unsigned char *value, unsigned char **p2);\nstatic int db_compare(unsigned char *query, unsigned char *string);\nstatic void db_get_elements(int lenstring2, unsigned char *string2, unsigned char *query, unsigned char *string);\nstruct query *db_query(char *query);\nstruct data *db_query_get_first(struct query *q);\nstruct data *db_data_get_next(struct data *d);\nint db_data_get_field(struct data *d, unsigned char *name2, int value2len, unsigned char *value2);\nstatic void db_parse_header(int headersize,unsigned char *header, unsigned char *string);\nvoid db_open(char *filename);<\/code><\/pre>\n\n\n\n<p>Seuraava kappale on fort:in satunnaisuuden ker\u00e4\u00e4mist\u00e4 varten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define DB2_EVENTS 2\nstatic unsigned int db2_events = 1;\nstatic unsigned int db2_event_mode = 6;\n\n#ifdef DB2_EVENTS\n#define DB2_EVENTS_START(source) \\\n  IUTIME micros; \\\n  static int \\\n    pool=0, pool2=0; \\\n  if(db2_events) { \\\n    fort_add_random_event_time(&amp;pool, \\\n    source, db2_event_mode); \\\n    fort_add_random_event_timer_start(&amp;micros); \\\n  }\n#else\n#define DB2_EVENTS_START(source)\n#endif\n\n#ifdef DB2_EVENTS\n#define DB2_EVENTS_END(source) \\\n  if(db2_events) \\\n    fort_add_random_event_timer_do(&amp;pool2, \\\n        source, db2_event_mode, &amp;micros);\n#else\n#define DB2_EVENTS_END(source)\n#endif\n<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 rutiini tulostaa muistissa olevat kyselyt:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dump_queries()\n{\n  struct query *q=first_query;\n  struct data *d;\n\n  while(q!=NULL) {\n    fprintf(stdout,\"Header: (addr:%p\",q);\n    fprintf(stdout,\", text=\\\"%s\\\"\",q->query);\n    fprintf(stdout,\", keys=\\\"%s\\\"\",q->keys);\n    fprintf(stdout,\", count=%ld\",q->count);\n    fprintf(stdout,\", first_data=%p\",q->first_data);\n    fprintf(stdout,\", next=%p\",q->next_query);\n    fprintf(stdout,\")\\n\");\n    fflush(stdout);\n\n    d=q->first_data;\n    while(d!=NULL) {\n      fprintf(stdout,\"Data:   (addr:%p\",d);\n      fprintf(stdout,\", data=\\\"%s\\\"\",d->data);\n      fprintf(stdout,\", next_data=%p\",d->next_data);\n      fprintf(stdout,\")\\n\");\n      fflush(stdout);\n      d=d->next_data;\n    }\n    q=q->next_query;\n  }\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Rutiini tallettaa merkkijonon ja palauttaa osoitteen siihen:<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>static unsigned char *save_string(unsigned char *string)\n{\n  unsigned char *temp=malloc(strlen(string)+1);\n  strcpy(temp, string);\n  return(temp);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 allokoidaan tila yhdelle data lohkolle:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static struct data *db_create_data(unsigned char *data)\n{\n  struct data *temp;\n\n  if((temp = malloc(sizeof(struct data))) != NULL) {\n    temp->data = save_string(data);\n    temp->changes = 0;\n    temp->next_data = NULL;\n    return(temp);\n  }\n  return(NULL);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 taas saadaan tila yhdelle query-rakenteelle:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static struct query *db_create_query(unsigned char *query)\n{\n  struct query *temp;\n\n  if((temp = malloc(sizeof(struct query))) != NULL) {\n    temp->query = save_string(query);\n    temp->keys = NULL;\n    temp->count = 0;\n    temp->first_data = NULL;\n    temp->next_query = NULL;\n  }\n  return(temp);\n}<\/code><\/pre>\n\n\n\n<p>Seuraava rutiini hakee halutun kyselyn ja palauttaa osoitteen siihen: aluksi while(*ppquery) luuppi etsii kysely\u00e4, joka on samanlainen parametrin\u00e4 annetun kyselyn kanssa. Jos sellainen l\u00f6ytyy, se poistetaan query ketjusta ja lis\u00e4t\u00e4\u00e4n takaisin ketjun alkuun (if found). N\u00e4in useimmin k\u00e4ytetyt kyselyt ovat ketjussa ensimm\u00e4isin\u00e4. Jos taas ketjua ei l\u00f6ydyy, kysely ketjun alkuun lis\u00e4t\u00e4\u00e4n uusi kysely (else). Rutiini palauttaa osoitteen query-rakenteeseen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static struct query *db_fetch_query(char *query)\n{\n  int found;\n  struct query **ppquery, *thisq;\n\n  ppquery = &amp;first_query;\n  found = 0;\n  while(*ppquery != NULL) {\n    if(!strcmp(query, (*ppquery)->query)) {\n      found = 1;\n      break;\n    }\n    ppquery = &amp;((*ppquery)->next_query);\n  }\n  if(found) {\n    \/\/ remove from list                                                                                                                                  \n    thisq = *ppquery;\n    *ppquery = thisq->next_query;\n    \/\/ add back to 1st of list                                                                                                                           \n    thisq->next_query = first_query;\n    first_query = thisq;\n  } else {\n    \/\/ create new                                                                                                                                        \n    thisq = db_create_query(query);\n    thisq->next_query = first_query;\n    first_query = thisq;\n  }\n\n  return(thisq);\n}<\/code><\/pre>\n\n\n\n<p>Seuraava rutiini lis\u00e4\u00e4 yhden kyselyn vastaustietueen data ketjuun. Jos tietue on jo ketjussa, sit\u00e4 ei lis\u00e4t\u00e4, ja jos tietuetta ei ole tietue luodaan (if (!found)). Ketjua yll\u00e4pidet\u00e4\u00e4n siten ett\u00e4 data ketju on aakkosj\u00e4rjestyksess\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static struct data *db_add_data(struct data **data, unsigned char *buf) {\n  int found;\n  struct data **pdata = data;\n  struct data *pnew;\n\n  DB2_EVENTS_START(202)\n\n  found=0;\n  while(*pdata!=NULL) {\n    if(!strcmp(buf,(*pdata)->data)) {\n      found = 1;\n      break;\n    }\n    if(strcmp(buf,(*pdata)->data)&lt;0) {\n      break;\n    }\n    pdata=&amp;((*pdata)->next_data);\n  }\n  if(!found) {\n    pnew = db_create_data(buf);\n    pnew -> next_data = *pdata;\n    *pdata = pnew;\n  }\n\n  DB2_EVENTS_END(203)\n\n  return(pnew);\n}\n\nstatic struct data *db_query_add_data(struct query *q, unsigned char *buf)\n{\n  if(db_add_data(&amp;q->first_data,buf) !=\n      NULL)\n    q->count++;\n}<\/code><\/pre>\n\n\n\n<p>Sitten funktio, jolla ohitetaan merkkijonosta v\u00e4lily\u00f6nnit ja tabit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static void db_skipwhite(unsigned char **p2)\n{\n  unsigned char *p;\n\n  p=*p2;\n  while(*p==' ' || *p=='\\t')\n    p++;\n  *p2=p;\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4  luetaan nimi merkkijonosta. Nimi koostuu heittomerkeist\u00e4 ja niiden v\u00e4liss\u00e4 olevista merkeist\u00e4. Merkkej\u00e4 ei aakkostarkasteta, joten nimess\u00e4 voi olla mit\u00e4 tahansa merkist\u00f6\u00e4 tahansa. My\u00f6sk\u00e4\u00e4n nimi\u00e4 (ja arvoja) ei viel\u00e4 unicode tarkasteta, pitkiss\u00e4 nimiss\u00e4 (tai arvoissa) ei ole merkkikatkoa. Unicodessa:han yksi merkki muodostuu useammista fyysisist\u00e4 merkeist\u00e4, ja jos kirjainmerkki katkaistaan kesken fyysisten merkkien voi tulla ongelmia.<\/p>\n\n\n\n<p>N\u00e4iss\u00e4 funktioissa paikka merkkijonossa kerrotaan p2 osoitteella. Sen sis\u00e4lt\u00f6 osoittaa seuraavaan parsittavaan merkkiin.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int db_parsename(int namelen, unsigned char *name, unsigned char **p2)\n{\n  int stat, ncount;\n  unsigned char *p, *n;\n\n  p=*p2;\n  n=name;\n  ncount=0;\n  stat=0;\n\n  db_skipwhite(&amp;p);\n  if(*p == '\\'') { \/\/ name with '                                                                                                                          \n    p++;\n    while(*p != '\\'' &amp;&amp; *p != '\\0') {\n      if(++ncount &lt; namelen)\n        *n++ = *p;\n      p++;\n      stat = 1;\n    }\n    if(*p == '\\'')\n      p++;\n  }\n  *n = '\\0';\n  *p2 = p;\n\n  return(stat);\n}<\/code><\/pre>\n\n\n\n<p>Arvon lukuun tarkoitettu rutiini: kuten kenttien nimiss\u00e4 t\u00e4m\u00e4kin voi koostua mist\u00e4 tahansa merkist\u00f6ist\u00e4. Arvo on aina tuplahipsujen (&#8220;) v\u00e4liss\u00e4. P2 muuttuja osoittaa merkkiin jota k\u00e4sitell\u00e4\u00e4n ja sinne p\u00e4ivitet\u00e4\u00e4n rutiinin lopussa seuraava merkki.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int db_parsevalue(int valuelen, unsigned char *value, unsigned char **p2)\n{\n  int stat, vcount;\n  unsigned char *p, *v;\n\n  p = *p2;\n  v = value;\n  vcount = 0;\n  stat = 0;\n\n  db_skipwhite(&amp;p);\n  if(*p == '\"') { \/\/ value with \" -- skip                                                                                                                  \n    p++;\n    while(*p != '\\\"' &amp;&amp; *p != '\\0') {\n      if(++vcount &lt; valuelen)\n         *v++ = *p;\n      p++;\n      stat = 2;\n    }\n    if(*p == '\\\"')\n      p++;\n  }\n  *v = '\\0';\n  *p2 = p;\n\n  return(stat);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 parsitaan nimi arvo pari. T\u00e4ss\u00e4 nimen ja arvon v\u00e4liss\u00e4 voi olla &#8216;=&#8217;.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int db_parse_nameandvalue(int namelen, unsigned char *name, int valuelen, unsigned char *value, unsigned char **p2)\n{\n  int stat;\n  unsigned char *p,*n,*v;\n\n  DB2_EVENTS_START(204)\n\n  p = *p2;\n  n = name;\n  *n = '\\0';\n  v = value;\n  *v = '\\0';\n  stat = 0;\n\n  db_skipwhite(&amp;p);\n  stat=db_parsename(namelen,name,&amp;p);\n  db_skipwhite(&amp;p);\n  if(*p == '=') {\n    p++;\n  }\n  db_skipwhite(&amp;p);\n  if(*p == '\\\"') {\n    stat=db_parsevalue(valuelen,value,&amp;p);\n  }\n  if(*p == ',') {\n    p++;\n  }\n  *p2 = p;\n\n  DB2_EVENTS_END(205)\n\n  return(stat);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 verrataan tietuetta kyselyyn ja palautetaan 1 jos tietue m\u00e4ts\u00e4\u00e4 0 jos ei.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int db_compare_all(unsigned char *query, unsigned char *string)\n{\n  int stat,stat2,ok,ok2;\n  unsigned char *q, *s;\n  unsigned char name&#91;MAXNAME], value&#91;MAXVALUE],\n      name2&#91;MAXNAME], value2&#91;MAXVALUE];\n\n  DB2_EVENTS_START(206)\n\n  ok = 1;\n  q = query;\n  db_skipwhite(&amp;q);\n  while((stat = db_parse_nameandvalue(\n      sizeof(name), name,\n      sizeof(value), value, &amp;q))>0) {\n    ok2 = 0;\n    s = string;\n    db_skipwhite(&amp;s);\n    while((stat2 = db_parse_nameandvalue(\n        sizeof(name2), name2,\n        sizeof(value2), value2, &amp;s))>0) {\n      ok2=0;\n      if(!strcmp(name, name2)) {\n        if(stat == 2 &amp;&amp; stat2 == 2) {\n          if(!strcmp(value, value2))\n            ok2 = 1;\n        } else if(stat==1 &amp;&amp; stat2==2) {\n          ok2 = 1;\n        }\n      }\n      if(ok2)\n        break;\n    }\n    if(!ok2)\n      ok = 0;\n  }\n\n  DB2_EVENTS_END(207)\n\n  return(ok);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 poimii kyselyn sis\u00e4lt\u00e4m\u00e4t elementit merkkijonosta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static void db_get_elements(int lenstring2, unsigned char *string2, unsigned char *query, unsigned char *string)\n{\n  int first,stat,stat2;\n  unsigned char name&#91;MAXNAME],\n      value&#91;MAXVALUE], name2&#91;MAXNAME],\n      value2&#91;MAXVALUE];\n  unsigned char *q,*s;\n\n  *string2='\\0';\n  first=1;\n  q=query;\n  db_skipwhite(&amp;q);\n  while((stat = db_parse_nameandvalue(\n      sizeof(name), name,\n      sizeof(value), value,&amp;q))>0) {\n    s = string;\n    db_skipwhite(&amp;s);\n    while((stat2 = db_parse_nameandvalue(\n        sizeof(name2), name2,\n        sizeof(value2), value2, &amp;s))>0) {\n      if(!strcmp(name, name2)) {\n        if((strlen(string2) + strlen(name2) +\n            strlen(value2) +5)&lt;lenstring2) {\n          if(!first)\n            strcat(string2,\", \");\n          strcat(string2,\"\\'\");\n          strcat(string2,name2);\n          strcat(string2,\"\\'\");\n          strcat(string2,\" \");\n          strcat(string2,\"\\\"\");\n          strcat(string2,value2);\n          strcat(string2,\"\\\"\");\n          first=0;\n        }\n      }\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 tarkistetaan sis\u00e4lt\u00e4\u00e4k\u00f6 merkkijono halutun elementin. T\u00e4t\u00e4 k\u00e4ytet\u00e4\u00e4n kun p\u00e4\u00e4tell\u00e4\u00e4n tietueen avaimet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int db_contains_element(char *name2, char *string)\n{\n  unsigned char *s;\n  unsigned char name&#91;MAXNAME],\n      value&#91;MAXVALUE];\n\n  s = string;\n  db_skipwhite(&amp;s);\n  while(db_parse_nameandvalue(\n      sizeof(name), name,\n      sizeof(value), value, &amp;s)>0) {\n    if(!strcmp(name, name2))\n      return(1);\n  }\n  return(0);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 rutiini suorittaa kyselyn. T\u00e4ll\u00e4 hetkell\u00e4 se on vain simppeli tiedoston luku, siihen on joukko muutoksia jonossa. (muut kyselyt pohjana, vierasavainten p\u00e4\u00e4ss\u00e4 olevien kenttien &#8220;join&#8221;, suurista tiedostoista vain osa muistissa, jne.)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static struct query *db_query2(char *query)\n{\n  struct query *q;\n  unsigned char\n      string&#91;8192],\n      string2&#91;8192],\n      header&#91;8192];\n\n  FILE *fp1;\n\n  DB2_EVENTS_START(208)\n\n  q = db_fetch_query(query);\n\n  if((fp1 = fopen(dbfilename, \"r\")) != NULL) {\n    while(fgets(string, sizeof(string), fp1) != NULL) {\n      string&#91;strlen(string)-1] = '\\0';\n      if(string&#91;0] != ';') {\n        if(db_compare_all(query, string)) {\n          db_get_elements(sizeof(string2), string2,\n              query, string);\n          db_query_add_data(q, string2);\n        }\n      }\n    }\n    fclose(fp1);\n  }\n\n  DB2_EVENTS_END(209)\n\n  return(q);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 funktio palauttaa tietueesta jonkin tietueen kent\u00e4n arvon:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int db_data_get_field(struct data *d, unsigned char *name2, int value2len, unsigned char *value2)\n{\n  int stat;\n  unsigned char name&#91;MAXNAME], value&#91;MAXVALUE];\n  unsigned char *p;\n\n  p = d->data;\n  db_skipwhite(&amp;p);\n  while((stat = db_parse_nameandvalue(sizeof(name), name,\n      sizeof(value), value, &amp;p))>0) {\n    if(!strncmp(name, name2,\n        strlen(name2))) {\n      strncpy(value2, value, value2len);\n      if(value2len &lt;= strlen(value))\n        value2&#91;value2len-1] = '\\0';\n      return(1);\n    }\n  }\n  return(0);\n}<\/code><\/pre>\n\n\n\n<p>Seuraava lis\u00e4\u00e4 kyselyyn listan kyselyn avaimista. T\u00e4t\u00e4 tarvitaan esimerkiksi save-toiminnossa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void db_update_keys(struct query *q)\n{\n  int found = 0;\n  unsigned char keys&#91;1024];\n  struct query *q2;\n  struct data *d2;\n\n  keys&#91;0] = '\\0';\n  q2=db_query2(\"'memberid', 'key' \\\"key\\\"\");\n  d2=db_query_get_first(q2);\n  while(d2 != NULL) {\n    int first = 1;\n    unsigned char memberid&#91;MAXNAME];\n\n    if(db_data_get_field(d2, \"memberid\", sizeof(memberid),\n        memberid)) {\n      if(db_contains_element(memberid, q->query)) {\n        if(!first)\n          strcat(keys, \", \");\n        strcat(keys, \"'\");\n        strcat(keys, memberid);\n\tstrcat(keys, \"'\");\n        found = 1;\n        first = 0;\n      }\n    }\n    d2 = db_data_get_next(d2);\n  }\n  if(found)\n    q->keys = save_string(keys);\n  else\n    q->keys = save_string(\"none\");\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4t\u00e4 rutiinia kutsutaan kyselyss\u00e4, ja se vain kutsuu kysely kakkosta. T\u00e4m\u00e4 se vuoksi ett\u00e4 kyselyn tulkinnassa voidaan tehd\u00e4 lis\u00e4kyselyj\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct query *db_query(char *query)\n{\n  struct query *q,*q2;\n  struct data *d2;\n\n  q = db_query2(query);\n\n  if(q->keys == NULL) {\n    db_update_keys(q);\n  }\n\n  return(q);\n}<\/code><\/pre>\n\n\n\n<p>Seuraavalla saadaan kyselyn palauttamasta query-tietueesta ensimm\u00e4inen kyselyn vastausrivi:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct data *db_query_get_first(struct query *q)\n{\n  return(q->first_data);\n}<\/code><\/pre>\n\n\n\n<p>Seuraavalla saadaan kyselyn data-tietueesta seuraava data tietue:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct data *db_data_get_next(struct data *d)\n{\n  return(d->next_data);\n}<\/code><\/pre>\n\n\n\n<p>Funktio palauttaa tietueesta tietueen kenttien nimet, eli kyselyn, jonka vastaus tietue on.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static void db_parse_header(int headersize,unsigned char *header, unsigned char *string)\n{\n  int first;\n  unsigned char *p, name&#91;MAXNAME], value&#91;MAXVALUE];\n\n  *header = '\\0';\n  first = 1;\n  p = string;\n  db_skipwhite(&amp;p);\n  while(db_parse_nameandvalue(sizeof(name), name,\n      sizeof(value), value, &amp;p)>0) {\n    if((strlen(header) + strlen(name) + 2) &lt;\n        headersize) {\n      if(!first)\n        strcat(header, \", \");\n      strcat(header, \"'\");\n      strcat(header, name);\n      strcat(header, \"'\");\n      first = 0;\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>Seuraavalla funktiolla avataan tietokanta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void db_open(char *filename)\n{\n  int stringlen = 8192, headerlen = 8192;\n  unsigned char *string, *header;\n  unsigned char buffer&#91;1024];\n  FILE *fp1;\n\n  string = malloc(stringlen);\n  header = malloc(headerlen);\n\n  strcpy(dbfilename, filename);\n\n  if((fp1 = fopen(dbfilename, \"r\"))\n      != NULL) {\n    while(fgets(string, stringlen, fp1)\n        != NULL) {\n      while(string&#91;strlen(string)-1] != '\\n') {\n        string = realloc(string,\n            stringlen*2);\n        fgets(string+stringlen-1,\n            stringlen+1, fp1);\n        stringlen *= 2;\n      }\n      string&#91;strlen(string)-1] = '\\0';\n      db_parse_header(sizeof(header),\n          header, string);\n      db_query(header);\n    }\n    fclose(fp1);\n  }\n\n  free(string);\n  free(header);\n\n  fprintf(stdout,\"\\n\");\n}<\/code><\/pre>\n\n\n\n<p>Seuraavaksi siirryt\u00e4\u00e4n DBS alkuisiin rutiineihin. Aluksi muutamia muuttujia:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>char *procname;\nchar *programname = \"DBS version 0.6 \u00a9\";\n\nunsigned char *htmlin;\nint htmlinlen=2048;\n\n#define HTMLTIMEFORMAT \"%a, %d %b %Y %H:%M:%S GMT\"\n\nunsigned char htmlmethod&#91;10];\nunsigned char htmlpath&#91;128];\nunsigned char htmlversion&#91;32];\nunsigned char htmlfilename&#91;64];\nunsigned char htmlsessionid&#91;33];\nunsigned char htmltime&#91;32];\nunsigned char htmluserid&#91;24];\nunsigned char htmlpassword&#91;24];\nunsigned char *htmlparams=NULL;\nunsigned char htmldigest&#91;HashLen*2+1];\nunsigned char htmlmode&#91;10];\nunsigned char htmlip&#91;32];\nunsigned char htmlport&#91;10];\nunsigned char htmllanguage&#91;10];\n#define TIMEFORMAT \"%Z%Y%m%d%H%M%S\"<\/code><\/pre>\n\n\n\n<p>N\u00e4ill\u00e4 ensimm\u00e4isill\u00e4 rutiineilla muodostetaan k\u00e4ytt\u00e4j\u00e4lle istuntoavain. Satunnaisbittien tekemiseen voidaan k\u00e4ytt\u00e4\u00e4 t\u00e4ss\u00e4 sek\u00e4 fort:ia ja ressua. Jos haluat kokeilla sovellusta k\u00e4yt\u00e4nn\u00f6ss\u00e4, mukaan on lis\u00e4tty kevytversio ressusta, n\u00e4in fort:ia ja ressua (+sha256) ei tarvitse kopioida.<\/p>\n\n\n\n<p>Session id annetaan istunnolle kun k\u00e4ytt\u00e4j\u00e4 sy\u00f6tt\u00e4\u00e4 k\u00e4ytt\u00e4j\u00e4tunnuksen ja salasanan onnistuneesti (dbs_logon). Session id:st\u00e4 tehd\u00e4\u00e4n kookie selaimelle.<\/p>\n\n\n\n<p>Ensimm\u00e4inen merkki session id:ss\u00e4 on pieni tai iso kirjain ja loput merkit ovat numeroita, pieni\u00e4 tai isoja kirjaimia. Pieni\u00e4 ja isoja kirjaimia on 26+26 on 52 ja siihen numerot, erilaisia merkkej\u00e4 on 62, eli yksi merkki on vajaat 6 bitti\u00e4. Erilaisia istunto id:it\u00e4 on 52*62^31=1.90e57.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define aFORT 2\n#define aRESSU 2\n\nunsigned char dbs_clockbyte() \/* JariK 2013 *\/\n{\n  struct timeval tv;\n\n  gettimeofday(&amp;tv,NULL);\n\n  return(tv.tv_usec &amp; 0xff);\n}\n\nvoid dbs_ressu_genbytes_fast(int size, unsigned char *buffer) \/* JariK 09\/2020 *\/\n{\n  int c, d, e, f, byte;\n\n  f=0;\n\n  for(c=0; c&lt;8 || c%8!=0 || c&lt;16 ; c++) {\n    for(d=0; d&lt;size; d++) {\n      e = buffer&#91;d];\n      e = ((e&amp;0x80)>>7) | ((e&amp;0x7f)&lt;&lt;1);\n      byte = dbs_clockbyte();\n      buffer&#91;d] = e^byte;\n    }\n    for(d=0; d&lt;size; d++) {\n      f = (f+buffer&#91;d])%size;\n      e = buffer&#91;d];\n      buffer&#91;d] = buffer&#91;f];\n      buffer&#91;f] = e;\n    }\n  }\n}\n\n#define RANDOMCNT 256\n#define aDBS_RANDOM_CLEAR 2\n\nstatic unsigned char dbs_random_bytes&#91;RANDOMCNT];\nstatic int dbs_random_byte = 999999999;\nstatic int dbs_random_cnt = RANDOMCNT;\n\nint dbs_random_genbyte()\n{\n  if(dbs_random_byte>=dbs_random_cnt) {\n#ifdef DBS_RANDOM_CLEAR\n    memset(dbs_random_bytes, 0,\n        dbs_random_cnt);\n#else\n    if(dbs_random_byte == 999999999)\n      memset(dbs_random_bytes, 0,\n          dbs_random_cnt);\n#endif\n#ifdef FORT\n    fort_random_data(dbs_random_cnt,\n        dbs_random_bytes);\n#endif\n#ifdef RESSU\n    ressu_genbytes(dbs_random_cnt,\n        dbs_random_bytes);\n#endif\n    dbs_ressu_genbytes_fast(dbs_random_cnt,\n        dbs_random_bytes);\n    dbs_random_byte = 0;\n  }\n  return(dbs_random_bytes&#91;\n      dbs_random_byte++]);\n}\n\nint dbs_random_genbyte_limit(int limit)\n{\n  int c;\n\n  while((c = dbs_random_genbyte()) >=\n      (256\/limit)*limit);\n  \/* while((c = fort_random_data_byte())>                                                                                                                                                                                                                                                                                                                                      \n      (256\/limit)*limit); little bug *\/\n  return(c % limit);\n}\n\nvoid dbs_random_clear()\n{\n  fort_clear();\n  ressu_clear();\n  memset(dbs_random_bytes, 0,\n      dbs_random_cnt);\n  dbs_random_byte = 999999998;\n}\n\nvoid dbs_random_genbuffer(int size, unsigned char *buffer)\n{\n  int c;\n\n  for(c = 0;c &lt; size;c++)\n    buffer&#91;c] ^= dbs_random_genbyte();\n}\n\nvoid dbs_gensessionid(int size, unsigned char *buffer)\n{\n  int len,byte,first;\n  unsigned char chars&#91;] =\n    \"0123456789\" \\\n    \"abcdefghijklmnopqrstuvwxyz\" \\\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\n  unsigned char *n;\n\n\n  dbs_random_clear();\n  n=buffer;\n  len=0;\n  first=1;\n  while(++len&lt;size) {\n    if(first)\n      byte=dbs_random_genbyte_limit(\n          sizeof(chars)-11)+10;\n    else\n      byte=dbs_random_genbyte_limit(\n          sizeof(chars)-1);\n    *n++=chars&#91;byte];\n    first=0;\n  }\n  *n='\\0';\n  dbs_random_clear();\n}<\/code><\/pre>\n\n\n\n<p>Tulostetaan kysely debukkausta varten:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_dump_query(struct query *q)\n{\n  struct data *d;\n\n  html_printf(\"query: %s&lt;br>\",q->query);\n  d = db_query_get_first(q);\n  while(d != NULL) {\n    html_printf(\"data: %s&lt;br>\",d->data);\n    d = db_data_get_next(d);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Seuraavalla rutiinilla lis\u00e4t\u00e4\u00e4n ohjelman kookie selaimen muistiin: kookiena talletetaan session id ja htmlmoodi. Max-Age kertoo kuinka kauan sekunteina kookie on k\u00e4ytt\u00f6kelpoinen, t\u00e4ss\u00e4 24 tuntia. Kookie p\u00e4ivitet\u00e4\u00e4n aina html l\u00e4hetyksen yhteydess\u00e4, joten kookie on k\u00e4ytt\u00f6kelpoinen 24 tuntia viimeisen html-lomakkeen vastaanotosta. Kookieta ei tarkasteta viel\u00e4 alkuper\u00e4iseen kookieen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#define DBS_RENEW_SESSION_HOURS 24\n\nvoid dba_renew_cookie()\n{\n  dbs_html_buf_printf(HTML_HEADER_BUFFER,\n    \"Set-Cookie: sessionid=%s; Max-Age=%d\\r\\n\",\n    htmlsessionid, 3600*DBS_RENEW_SESSION_HOURS);\n  dbs_html_buf_printf(HTML_HEADER_BUFFER,\n    \"Set-Cookie: mode=%s; Max-Age=%d\\r\\n\",\n    htmlmode, 3600*DBS_RENEW_SESSION_HOURS);\n  dbs_html_buf_printf(HTML_HEADER_BUFFER,\n    \"Set-Cookie: language=%s; Max-Age=%d\\r\\n\",\n    htmllanguage, 3600*DBS_RENEW_SESSION_HOURS);\n}<\/code><\/pre>\n\n\n\n<p>Seuraavana uudelleen paketoidut tietokantarutiinit: rutiinit on paketoitu uudelleen sit\u00e4 varten ett\u00e4 saadaan html-raportti tehdyist\u00e4 kyselyist\u00e4 ja niiden sis\u00e4ll\u00f6st\u00e4: raportti kirjoitetaan dbs_html rutiinin kakkospuskuriin. Puskuria k\u00e4sitet\u00e4\u00e4n seuraavan rutiinin lis\u00e4ksi dba_main() rutiinissa ja tietenkin dbs_html rutiineissa.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>struct query *dba_query(unsigned char *query)\n{\n  int save_html;\n  struct query *q;\n  struct data *d;\n\n  \/\/ query statements and data                                                                                                                \n  dbs_html_buf_printf(HTML_QUERY_BUFFER,\n      \"query: %s\\n&lt;br>\", query);\n  q=db_query(query);\n  d=db_query_get_first(q);\n  while(d!=NULL) {\n    dbs_html_buf_printf(HTML_QUERY_BUFFER,\n        \"data: %s\\n&lt;br>\",d->data);\n    d = db_data_get_next(d);\n  }\n  return(q);\n}\n\nstruct data *dba_query_get_first(struct query *q)\n{\n  return(db_query_get_first(q));\n}\n\nstruct data *dba_data_get_next(struct data *d)\n{\n  return(db_data_get_next(d));\n}\n<\/code><\/pre>\n\n\n\n<p>get_seconds rutiini on jo vanha tuttu:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned long dbs_getseconds()\n{\n  struct timeval tv;\n\n  gettimeofday(&amp;tv, NULL);\n\n  return((IUTIME)tv.tv_sec);\n}<\/code><\/pre>\n\n\n\n<p>Seuraava aliohjelma tulostaa printf tyyppisell\u00e4 m\u00e4\u00e4rityksell\u00e4 tehdyn merkkijonon html-puskuriin. Dbs_html_set funktiolla valitaan aktiivinen puskuri eli mihin puskuriin html &#8220;rivi&#8221; tulostetaan, dbs_html_clear rutiinilla t\u00e4m\u00e4nhetkinen puskuri tyhjennet\u00e4\u00e4n ja dbs_html_printf (ennen html_printf) rutiini tulostaa html merkkijonon ja lis\u00e4\u00e4 sen t\u00e4m\u00e4n hetkiseen puskuriin. html_printf rutiinin ensimm\u00e4inen kappale tulostaa html merkkijonon kasvattaen tulostusaluetta (dbs_printbuf ja dbs_printbuf_len) jos se on tarpeen. Toinen kappale kasvattaa varsinaista puskuria, jos kasvatus on tarpeen. Kolmas kappale eli tuo strcpy kopioi html lauseen tarvittaessa kasvatettuun puskuriin.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdarg.h>\n\nint dbs_html_now = 0;\nunsigned char *dbs_html&#91;] =\n    { NULL, NULL, NULL, NULL, NULL };\nint dbs_html_size&#91;] =\n    { 0, 0, 0, 0, 0 };\n#define HTML_BUFFERS 5\n#define HTML_HEADER_BUFFER 0\n#define HTML_PAYLOAD_BUFFER 1\n#define HTML_FOREIGN_BUFFER 2\n#define HTML_QUERY_BUFFER 3\n#define HTML_LAST_BUFFER 4\n\nvoid dbs_html_set(int html)\n{\n  dbs_html_now=html;\n  if(dbs_html&#91;html]==NULL) {\n    dbs_html_size&#91;html] = 128;\n    dbs_html&#91;html]=malloc(\n        dbs_html_size&#91;html]);\n  }\n}\n\nint dbs_html_get()\n{\n  return(dbs_html_now);\n}\n\nvoid dbs_html_clear()\n{\n  dbs_html&#91;dbs_html_now]&#91;0]='\\0';;\n}\n\n\/\/ old name works also\n#define html_printf dbs_html_printf\n\nvoid dbs_html_printf_valist(const char *format, va_list ap)\n{\n  int count;\n  static char *dbs_printbuf=NULL;\n  static int dbs_printbuf_len=0;\n\n  va_list ap2;\n  va_copy(ap2,ap);\n\n  count=vsnprintf(dbs_printbuf, dbs_printbuf_len, format, ap) + 1;\n  if(dbs_printbuf_len &lt; count) {\n    dbs_printbuf_len = count;\n    dbs_printbuf=realloc(dbs_printbuf, dbs_printbuf_len);\n    count=vsnprintf(dbs_printbuf, dbs_printbuf_len,\n        format, ap2) + 1;\n  }\n\n  if(dbs_html_size&#91;dbs_html_now] &lt;\n     strlen(dbs_html&#91;dbs_html_now]) +\n     strlen(dbs_printbuf) + 1) {\n    dbs_html_size&#91;dbs_html_now] =\n     strlen(dbs_html&#91;dbs_html_now]) +\n     strlen(dbs_printbuf) + 1;\n    dbs_html&#91;dbs_html_now] = realloc(dbs_html&#91;dbs_html_now],\n     dbs_html_size&#91;dbs_html_now]);\n  }\n\n  strcpy(dbs_html&#91;dbs_html_now]+\n      strlen(dbs_html&#91;dbs_html_now]), dbs_printbuf);\n}\n\n\nvoid dbs_html_printf(const char *format, ...)\n{\n  va_list args;\n\n  va_start(args, format);\n  dbs_html_printf_valist(format, args);\n  va_end(args);\n}\n\nvoid dbs_html_buf_printf(int bufno, const char *format, ...)\n{\n  int save_html;\n  va_list args;\n\n  save_html=dbs_html_get();\n  dbs_html_set(bufno);\n  va_start(args, format);\n  dbs_html_printf_valist(format, args);\n  va_end(args);\n  dbs_html_set(save_html);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ll\u00e4 luetaan html parametri:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int dbs_parse_hex1(unsigned char **str) \/* 20131003 JariK *\/\n{\n  char *p;\n  int digit;\n\n  p=*str;\n  digit = 0;\n  if(*p>='a' &amp;&amp; *p&lt;='f')\n    digit=*p-'a'+10, p++;\n  else if(*p>='A' &amp;&amp; *p&lt;='F')\n    digit=*p-'A'+10, p++;\n  else if(*p>='0' &amp;&amp; *p&lt;='9')\n    digit=*p-'0', p++;\n  else digit=-1;\n\n  *str = p;\n  return(digit);\n}\n\nint dbs_parse_html_string(int stringlen, unsigned char *string, unsigned char **html)\n{\n  int c,d,count,ok;\n  unsigned char *s=string;\n  unsigned char *h=*html;\n\n  ok=0;\n  count=0;\n\n  while(isalnum(*h)||*h=='%'||*h=='+'||\n      *h=='-'||*h=='_' || *h=='.' ||\n      *h=='*') {\n    ok=1;\n    if(*h=='%') {\n      h++;\n      if((c=dbs_parse_hex1(&amp;h)) >= 0) {\n        if((d=dbs_parse_hex1(&amp;h)) >= 0) {\n          c = c*16+d;\n        } else\n          c = c;\n      } else\n        c = 64;\n      if(++count &lt; stringlen)\n        *s++ = c;\n    } else if(*h=='+') {\n      if(++count &lt; stringlen)\n        *s++ = ' ';\n      h++;\n    } else {\n      if(++count &lt; stringlen)\n        *s++ = *h;\n      h++;\n    }\n  }\n  *s = '\\0';\n  *html = h;\n\n  return(ok);\n}\n\nint dbs_parse_html_parameter(int namelen, unsigned char *name, int valuelen, unsigned char *value, unsigned char **s)\n{\n  DB2_EVENTS_START(300)\n\n  dbs_parse_html_string(namelen, name, s);\n  if((**s)=='=')\n    (*s)++;\n  dbs_parse_html_string(valuelen, value, s);\n  if(**s=='&amp;')\n    (*s)++;\n\n  DB2_EVENTS_END(301)\n}\n\nint dbs_get_parameter(char *name, int valuelen, unsigned char *value)\n{\n  int ok;\n  unsigned char name2&#91;32];\n  unsigned char *h = htmlparams;\n\n  DB2_EVENTS_START(302)\n\n  value&#91;0] = '\\0';\n  ok = 0;\n\n  while(*h!='\\0') {\n    dbs_parse_html_parameter(sizeof(name2),\n        name2, valuelen, value, &amp;h);\n    if(!strcmp(name,name2)) {\n      ok = 1;\n      break;\n    }\n  }\n\n  DB2_EVENTS_END(303)\n\n  return(ok);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4ss\u00e4 dbs:n logon n\u00e4ytt\u00f6: N\u00e4ytt\u00f6 antaa k\u00e4ytt\u00e4j\u00e4lle k\u00e4ytt\u00e4j\u00e4 ja salasana sy\u00f6tt\u00f6ikkunat ja tarkastaa annetut vastaukset ja tekee istuntoavaimen (sessionid).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_logon()\n{\n  unsigned char htmltext&#91;128];\n  unsigned char userid&#91;32];\n  unsigned char password&#91;32];\n\n  if(dbs_get_parameter(\"userid-0\",\n      sizeof(userid), userid)==1 &amp;&amp;\n     dbs_get_parameter(\"password-0\",\n      sizeof(password), password)==1) {\n    if(!strcmp(userid,\"testi\") &amp;&amp;\n        !strcmp(password,\"testaus\")) {\n      dbs_gensessionid(\n          sizeof(htmlsessionid),\n          htmlsessionid);\n      dbs_get_parameter(\"language-0\",\n          sizeof(htmllanguage),\n          htmllanguage);\n      dba_renew_cookie();\n\n      fprintf(stdout,\"Userid=\\\"%s\\\"\",\n          userid);\n      fprintf(stdout,\", password=\\\"%s\\\"\",\n          password);\n      fprintf(stdout,\", language=\\\"%s\\\"\",\n          htmllanguage);\n      fprintf(stdout,\", sessionid=\\\"%s\\\"\",\n          htmlsessionid);\n      fprintf(stdout,\"\\n\");\n\n      FILE *fp1;\n\n      if((fp1 = fopen(\"dbssessions.deb\", \"a\")) != NULL) {\n        fprintf(fp1,\"sessionid=%s\",\n            htmlsessionid);\n        fprintf(fp1,\", user=%s\",\n            userid);\n        fprintf(fp1,\", language=%s\",\n            htmllanguage);\n        fprintf(fp1,\", logontime=%s\",\n            htmltime);\n        fprintf(fp1,\", ip=%s\",\n            htmlip);\n        fprintf(fp1,\"\\n\");\n        fclose(fp1);\n      }\n    }\n  }\n\n  if(*htmlsessionid=='\\0') {\n\n    sprintf(htmltext,\"&lt;form action=\\\"logon\\\" method=\\\"POST\\\">\");\n    dbs_html_printf(htmltext);\n\n    dbs_html_printf(\"&lt;table border=\\\"0\\\">\");\n    dbs_html_printf(\"&lt;tr>\");\n    dbs_html_printf(\"Testiohjelmaan lokkaantuminen\"\n        \" Userid=testi, password=testaus\");\n    dbs_html_printf(\"&lt;\/tr>\");\n    dbs_html_printf(\"&lt;td>\");\n    dbs_html_printf(\"Userid\");\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;td>\");\n\n    dbs_html_printf(\"&lt;input type=\\\"char\\\"\"\n        \" name=\\\"userid-0\\\" value=\\\"%s\\\">\",\n        htmluserid);\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;\/tr>\");\n\n    dbs_html_printf(\"&lt;tr>\");\n    dbs_html_printf(\"&lt;td>\");\n    dbs_html_printf(\"Password\");\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;td>\");\n    dbs_html_printf(\"&lt;input type=\\\"password\\\"\"\n        \" name=\\\"password-0\\\" value=\\\"%s\\\">\",\n        htmlpassword);\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;\/tr>\");\n\n    dbs_html_printf(\"&lt;tr>\");\n    dbs_html_printf(\"&lt;td>\");\n    dbs_html_printf(\"Language\");\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;td>\");\n    dbs_html_printf(\"&lt;input type=\\\"char\\\"\"\n        \" name=\\\"language-0\\\" value=\\\"%s\\\"\"\n        \" size=\\\"3\\\">\",htmllanguage);\n    dbs_html_printf(\"&lt;\/td>\");\n    dbs_html_printf(\"&lt;\/tr>\");\n\n    dbs_html_printf(\"&lt;\/table>\");\n\n    dbs_html_printf(\"&lt;input type=\\\"submit\\\" value=\\\"Submit\\\">\");\n\n    dbs_html_printf(\"&lt;\/form>\");\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Luetaan html merkkijono:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int html_get_string(unsigned char *string,unsigned char **str,int len)\n{\n  int c,d,ok,count;\n  unsigned char *p,*n;\n\n  n=string;\n  p=*str;\n  ok=0;\n\n  count=0;\n  while(isalnum(*p)||*p=='%' ||*p=='+' ||\n      *p=='-' || *p=='_' || *p=='=' ||\n      *p=='&amp;' || *p=='.' || *p=='*') {\n    ok = 1;\n    if(*p=='%') {\n      p++;\n      if((c=parse_hex1(&amp;p))>=0) {\n\tif((d=parse_hex1(&amp;p))>=0) {\n          c=c*16+d;\n        } else\n          c=c;\n      } else\n\tc=64;\n      if(count++&lt;len)\n        *n++=c;\n    } else if(*p=='+') {\n      if(count++&lt;len)\n\t*n++=' ';\n      p++;\n    } else {\n      if(count++&lt;len)\n\t*n++=*p++;\n      else\n        p++;\n    }\n  }\n  *n='\\0';\n  *str=p;\n\n  return(ok);\n}\n<\/code><\/pre>\n\n\n\n<p>Seuraavassa translate ohjelma, jolla k\u00e4\u00e4nnet\u00e4\u00e4n memberid kenttien arvot muille kielille:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int dba_app_parse_word(int *num, int wordlength, unsigned char *word, unsigned char **p2)\n{\n  int count,stat;\n  unsigned char *p;\n  unsigned char *w;\n\n  char *colon = \"\uff1a\";\n  char *semicolon = \"\uff1b\";\n  char *comma = \"\uff0c\";\n\n  stat=0;\n  p = *p2;\n  db_skipwhite(&amp;p);\n  *num = 0;\n  while(*p>='0' &amp;&amp; *p&lt;='9') {\n    *num=(*num)*10+(*p-'0');\n    p++;\n    stat=1;\n  }\n  db_skipwhite(&amp;p);\n  if(*p==':')\n    p++;\n  if(!strncmp(p,colon,strlen(colon)))\n    p+=strlen(colon);\n\n  db_skipwhite(&amp;p);\n  count=0;\n  w=word;\n  while(*p!='\\0' &amp;&amp; *p!=',' &amp;&amp;\n        strncmp(p,comma,strlen(comma)) &amp;&amp;\n        strncmp(p,semicolon,strlen(semicolon)) &amp;&amp; *p!='\\n') {\n    if(++count &lt; wordlength)\n      *w++ = *p;\n    p++;\n  }\n  if(*p==',')\n    p++;\n  if(!strncmp(p,semicolon,strlen(semicolon)))\n    p+=strlen(semicolon);\n  if(!strncmp(p,comma,strlen(comma)))\n    p+=strlen(comma);\n  db_skipwhite(&amp;p);\n  *w='\\0';\n\n  *p2 = p;\n\n  return(stat);\n}\n<\/code><\/pre>\n\n\n\n<p>Seuraavana varsinainen k\u00e4\u00e4nn\u00f6srutiini:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_translations()\n{\n  int first;\n  unsigned char memberid&#91;32];\n  struct query *q;\n  struct data *d;\n\n  int c;\n\n  first=1;\n  c=0;\n  fprintf(stdout,\"fi \");\n  q=dba_query(\"'memberid'\");\n  d=dba_query_get_first(q);\n  while(d!=NULL) {\n    if(db_data_get_field(d, \"memberid\",\n        sizeof(memberid), memberid)) {\n      if(!first)\n        fprintf(stdout,\", \");\n      fprintf(stdout,\"%d:%s\",c,memberid);\n      first=0;\n    }\n    c++;\n    d=dba_data_get_next(d);\n  }\n  fprintf(stdout,\"\\n\");\n\n  FILE *fp1;\n  unsigned char *f,*t,*l;\n  unsigned char frombuffer&#91;4096];\n  unsigned char tobuffer&#91;4096];\n  unsigned char fromword&#91;1024];\n  unsigned char toword&#91;1024];\n  int fromid;\n  int toid;\n  int stat,stat2;\n\n  if((fp1=fopen(\"translations.dat\",\"r\"))!=NULL) {\n    fgets(frombuffer,sizeof(frombuffer),fp1);\n    frombuffer&#91;strlen(frombuffer)-1]='\\0';\n    while(fgets(tobuffer,sizeof(tobuffer),fp1) !=NULL) {\n      tobuffer&#91;strlen(tobuffer)-1]='\\0';\n      fprintf(stdout,\"%s\\n\",tobuffer);\n      t=tobuffer;\n      db_skipwhite(&amp;t);\n      l=language;\n      while(isalpha(*t))\n        *l++=*t++;\n      *l='\\0';\n      db_skipwhite(&amp;t);\n\n      while(*t!='\\0') {\n        if((stat=dba_app_parse_word(&amp;toid, sizeof(toword), toword, &amp;t))!=1)\n          break;\n        f=frombuffer;\n        db_skipwhite(&amp;f);\n        while(isalpha(*f))\n          f++;\n        db_skipwhite(&amp;f);\n        while(*f!='\\0') {\n          if((stat2=dba_app_parse_word(&amp;fromid, sizeof(fromword), fromword, &amp;f))!=1)\n            break;\n          if(toid==fromid) {\n            fprintf(stdout,\"'word' \\\"%s\\\"\",fromword);\n            fprintf(stdout,\", 'language' \\\"%s\\\"\",language);\n            fprintf(stdout,\", 'translation' \\\"%s\\\"\\n\",toword);\n            fflush(stdout);\n            break;\n          }\n          db_skipwhite(&amp;f);\n        }\n      }\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>Nappuloiden tulostus:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_buttons()\n{\n  int lineno=0;\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Submit\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Fetch\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Reset\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Display\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Change\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Check\\\">\", lineno);\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func-%d\\\" value=\\\"Save\\\">\", lineno);\n}\n<\/code><\/pre>\n\n\n\n<p>Seuraavassa sovelluskoodi sy\u00f6tt\u00f6lomakkeille. Ensin k\u00e4ytetyt muuttujat:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>static int lineno;\nstatic int formerrors = 0;\nstatic unsigned char htmlstring&#91;1024];\nstatic unsigned char htmltext&#91;128];\nstatic unsigned char query&#91;128];\nstatic unsigned char app&#91;32];\nstatic unsigned char appname&#91;32];\nstatic unsigned char chapter&#91;32];\nstatic unsigned char memberid&#91;32];\nstatic unsigned char value&#91;128];\nstatic unsigned char parameterid&#91;32];\nstatic unsigned char parametervalue&#91;128];\nstatic unsigned char func&#91;10];\nstatic struct query *qappname;\nstatic struct data *dappname;\nstatic struct query *qchapter;\nstatic struct data *dchapter;\nstatic struct query *qheadermemberid;\nstatic struct query *qlinesmemberid;\nstatic struct query *qmemberid;\nstatic struct data *dmemberid;\nstatic struct query *qheaderdata;\nstatic struct query *qlinesdata;\nstatic struct query *qdata;\nstatic struct data *ddata;\n\nstatic int dba_app_foreign_keys_length=0;\nstatic unsigned char *dba_app_foreign_keys=NULL;\nstatic int dba_app_foreign_query_length = 0;\nstatic unsigned char *dba_app_foreign_query = NULL;\nstatic int dba_app_foreign_data_length = 0;\nstatic unsigned char *dba_app_foreign_data = NULL;\nstatic int dba_app_foreign_data_query_length = 0;\nstatic unsigned char *dba_app_foreign_data_query = NULL;\n<\/code><\/pre>\n\n\n\n<p>Seuraavassa koodi, joilla hallitaan vaihtelevan mittaisia, automaattisesti kasvatettavia merkkijonoja. dba_string_free() tyhjent\u00e4\u00e4 ja nollaa pituus ja merkkijonokent\u00e4t. Dba_string_realloc on sis\u00e4inen rutiini kahdelle viimeiselle. Se kasvattaa merkkijonolle varattua tilaa jos tarpeen. dba_string_set antaa alkuarvon merkkijonolle, sit\u00e4 k\u00e4ytet\u00e4\u00e4n yleens\u00e4 merkkijonon alkutyhjennykseen. Alkuarvo otetaan kolmannesta parametrist\u00e4. Dba_string_add lis\u00e4\u00e4 merkkijonon per\u00e4\u00e4n toisen kolmantena parametrina olevan merkkijonon. Ensimm\u00e4inen ja toinen parametri ovat talletuspaikat merkkijonon pituudelle ja merkkijonoon osoittavalle osoitteelle. J\u00e4tin viel\u00e4 debukkailuun k\u00e4ytetyt fprintf:t.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_string_free(int *length, unsigned char **string)\n{\n  if(*string!=NULL)\n    free(*string);\n  *string=NULL;\n  *length=0;\n}\n\n#define aDEBUG23 2\n\nvoid dba_string_realloc(int *length, unsigned char **string, unsigned char *string2)\n{\n  int totallength = ((*string==NULL)? 0: strlen(*string)) + strlen(string2) + 1;\n\n  if(*length &lt; totallength) {\n    *string = realloc(*string, totallength);\n    if(*length == 0)\n      *string&#91;0] = '\\0';\n    *length = totallength;\n  }\n}\n\nvoid dba_string_set(int *length, unsigned char **string, unsigned char *string2)\n{\n  dba_string_realloc(length, string, string2);\n\n  strcpy(*string, string2);\n\n#ifdef DEBUG23\n  fprintf(stdout,\"length: %d\", *length);\n  fprintf(stdout,\", data: %s(%ld)\", *string, strlen(*string));\n  fprintf(stdout,\", string: %s(%ld)\", string2, strlen(string2));\n  fprintf(stdout,\"\\n\");\n  fflush(stdout);\n#endif\n}\n\nvoid dba_string_add(int *length, unsigned char **string, unsigned char *string2)\n{\n  dba_string_realloc(length, string, string2);\n\n  strcat(*string, string2);\n\n#ifdef DEBUG23\n  fprintf(stdout,\"length: %d\", *length);\n  fprintf(stdout,\", data: %s(%ld)\", *string, strlen(*string));\n  fprintf(stdout,\", string: %s(%ld)\", string2, strlen(string2));\n  fprintf(stdout,\"\\n\");\n  fflush(stdout);\n#endif\n}<\/code><\/pre>\n\n\n\n<p>Seuraavassa aliohjelma, joka hakee sovelluksen vierasavaimet. Se hakee vierasavaimet merkkijonoon jonka perusteella voidaan siirrell\u00e4 vierasavainkent\u00e4t. Esimerkiksi tilausohjelmalla jono voi olla seuraavanlainen: Huomaa ett\u00e4 eri sovellusten kent\u00e4t on eroteltu puolipisteell\u00e4 ja ett\u00e4 merkkijono p\u00e4\u00e4ttyy puolipisteeseen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> 'tilauksen asiakasnumero' = \"asiakasnumero\", 'tilauksen asiakkaan nimi' = \"asiakkaan nimi\"; 'tilauksen tuotenumero' = \"tuotenumero\", 'tilauksen tuotteen hinta' = \"tuotteen hinta\", 'tilauksen tuotteen nimi' = \"tuotteen nimi\";<\/code><\/pre>\n\n\n\n<p>T\u00e4ss\u00e4 tilaus tapauksessa sen vierassovellukset on m\u00e4\u00e4ritetty seuraavasti: eli tilaukseen tulee tietoja asiakas jonoista ja tuotejonoista.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'app' \"tilaus\", 'fromapp' \"asiakas\", 'toapp' \"tilaus\"\n'app' \"tilaus\", 'fromapp' \"tuote\", 'toapp' \"tilaus\"<\/code><\/pre>\n\n\n\n<p>Ohjelma toimii siten, ett\u00e4 se hakee kaikki from sovelluksen ja to sovelluksen kent\u00e4t, ja etsii sielt\u00e4 kent\u00e4t, joilla on pisin yhteinen merkkijono ja merkkijono on olemassaoleva kentt\u00e4. Eri sovellukset erotellaan puolipisteill\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int dba_app_get_foreign_keys(int foreign_keys_length, unsigned char *foreign_keys, unsigned char *app)\n{\n  int count, first, separatorneeded;\n\n  struct query *qfromapp;\n  struct data *dfromapp;\n  struct query *qfrommemberid;\n  struct data *dfrommemberid;\n  struct query *qtomemberid;\n  struct data *dtomemberid;\n  struct query *qallmemberid;\n  struct data *dallmemberid;\n\n  if(foreign_keys_length>0)\n    foreign_keys&#91;0]='\\0';\n  first=1;\n  separatorneeded=0;\n  count=0;\n\n  sprintf(query, \"'memberid'\");\n  qallmemberid = dba_query(query);\n\n  sprintf(query,\"'app', 'fromapp', 'toapp' \\\"%s\\\"\",app);\n  qfromapp=dba_query(query);\n  dfromapp = dba_query_get_first(qfromapp);\n  while(dfromapp != NULL) {\n    unsigned char app2&#91;32], fromapp2&#91;32], toapp2&#91;32];\n    unsigned char frommemberid&#91;32], tomemberid&#91;32],\n      allmemberid&#91;32];\n    unsigned char savefrommemberid&#91;32], savetomemberid&#91;32];\n    db_data_get_field(dfromapp,\"app\",\n        sizeof(app2), app2);\n    db_data_get_field(dfromapp,\"fromapp\",\n        sizeof(fromapp2),fromapp2);\n    db_data_get_field(dfromapp, \"toapp\",\n        sizeof(toapp2),toapp2);\n    if(!strcmp(toapp2, app2)) {\n      sprintf(query,\"'app' \\\"%s\\\", 'chapter', 'memberid'\",\n          fromapp2);\n      qfrommemberid = dba_query(query);\n\n      sprintf(query,\"'app' \\\"%s\\\", 'chapter', 'memberid'\",\n          toapp2);\n      qtomemberid = dba_query(query);\n\n      dfrommemberid = dba_query_get_first(qfrommemberid);\n      while(dfrommemberid != NULL) {\n        int longest;\n        db_data_get_field(dfrommemberid, \"memberid\",\n            sizeof(frommemberid), frommemberid);\n        longest = 0;\n        savetomemberid&#91;0] = '\\0';\n        savefrommemberid&#91;0] = '\\0';\n\n        dtomemberid = dba_query_get_first(qtomemberid);\n        while(dtomemberid != NULL) {\n          db_data_get_field(dtomemberid, \"memberid\",\n              sizeof(tomemberid), tomemberid);\n          dallmemberid=dba_query_get_first(qallmemberid);\n          while(dallmemberid != NULL) {\n            db_data_get_field(dallmemberid, \"memberid\",\n                sizeof(allmemberid), allmemberid);\n            if((strstr(frommemberid, allmemberid) != NULL) &amp;&amp;\n               (strstr(tomemberid, allmemberid) != NULL)) {\n              if(longest &lt; strlen(allmemberid)) {\n                longest = strlen(allmemberid);\n                strcpy(savetomemberid, tomemberid);\n                strcpy(savefrommemberid, frommemberid);\n              } \/\/ if(longest &lt;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      \n            }\n            dallmemberid = dba_data_get_next(dallmemberid);\n          } \/\/ dallmemberid != NULL) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               \n          dtomemberid = dba_data_get_next(dtomemberid);\n        } \/\/ while(dtomemberid                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n        if(longest > 0) {\n#define FOREIGN_KEYS_ADD_STRING(string) \\\n          if(count + strlen(string) &lt; foreign_keys_length) { \\\n            strcat(foreign_keys, string); \\\n          } \\\n          count+=strlen(string);\n\n          if(!first) {\n            if(separatorneeded)\n              FOREIGN_KEYS_ADD_STRING(\",\");\n            FOREIGN_KEYS_ADD_STRING(\" \");\n          }\n\n          FOREIGN_KEYS_ADD_STRING(\"'\");\n          FOREIGN_KEYS_ADD_STRING(savetomemberid);\n          FOREIGN_KEYS_ADD_STRING(\"'\");\n          FOREIGN_KEYS_ADD_STRING(\" = \");\n          FOREIGN_KEYS_ADD_STRING(\"\\\"\");\n          FOREIGN_KEYS_ADD_STRING(savefrommemberid);\n          FOREIGN_KEYS_ADD_STRING(\"\\\"\");\n          first=0;\n          separatorneeded=1;\n        }\n        dfrommemberid = dba_data_get_next(dfrommemberid);\n      } \/\/ while(dfrommemberid != NULL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               \n      FOREIGN_KEYS_ADD_STRING(\";\");\n      separatorneeded=0;\n    } \/\/ if(!strcmp(toapp2                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n    dfromapp = dba_data_get_next(dfromapp);\n  } \/\/ dfromapp != NULL                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n  fprintf(stdout,\"foreign keys count:%d:\\n\",count);\n  return(count);\n}\n<\/code><\/pre>\n\n\n\n<p>Seuraava rutiini db_app_get_foreign_query muodostaa edellisen funktion foreign_keys tulosteesta ja lomakkeen tietueesta kyselyj\u00e4, joilla voidaan hakea vierassovelluksen tiedot t\u00e4h\u00e4n sovellukseen. T\u00e4ss\u00e4kin kyselyjen (eli sovellusten) v\u00e4lill\u00e4 on puolipiste.<\/p>\n\n\n\n<p>Esimerkkin\u00e4 t\u00e4ll\u00e4isest\u00e4 keys-jonosta:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code> 'tilauksen asiakasnumero' = \"asiakasnumero\", 'tilauksen asiakkaan nimi' = \"asiakkaan nimi\"; 'tilauksen tuotenumero' = \"tuotenumero\", 'tilauksen tuotteen hinta' = \"tuotteen hinta\", 'tilauksen tuotteen nimi' = \"tuotteen nimi\";<\/code><\/pre>\n\n\n\n<p>Rutiini tekee t\u00e4ll\u00e4isen: t\u00e4ss\u00e4 tilauksen otsikko<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'asiakasnumero' \"1000\", 'asiakkaan nimi' \"Firma 1\"<\/code><\/pre>\n\n\n\n<p>Ja tilausriville merkkijono on: (N\u00e4iss\u00e4 esimerkeiss\u00e4 n\u00e4yt\u00f6ll\u00e4 ei ollut tyhji\u00e4 kentti\u00e4:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'tuotenumero' \"2000\", 'tuotteen hinta' \"5,00\", 'tuotteen nimi' \"Tappi\"<\/code><\/pre>\n\n\n\n<p>Seuraavassa asiakkaan nimi, tuotteen hinta ja tuotteen nimi ovat tyhji\u00e4: Lomakkeen kenttien t\u00e4ytt\u00e4miseksi tarvitaan dba_app_foreign_data() funktiota, jota ei t\u00e4ss\u00e4 viel\u00e4 ole.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'asiakasnumero' \"1000\", 'asiakkaan nimi';\n'tuotenumero' \"2000\", 'tuotteen hinta', 'tuotteen nimi'<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_get_parameter_id(unsigned char *parameterid, unsigned char *name)\n{\n  snprintf(parameterid,32,\"%s-%d\", name, lineno);\n}\n\nint dba_app_get_foreign_query2(int foreign_query_length, unsigned char *foreign_query,unsigned char *foreign_keys, unsigned char *data)\n{\n  int first, count, semicolonneeded, semicolonprinted;\n  unsigned char fromname&#91;32], toname&#91;32];\n  unsigned char name&#91;32], value&#91;1024];\n  unsigned char *f,*d;\n\n  if(foreign_query_length>0)\n    foreign_query&#91;0]='\\0';\n  first=1;\n  count=0;\n  semicolonprinted=0;\n\n  fprintf(stdout,\"\\nget_foreign_query: foreign_query_length:%d\", foreign_query_length);\n  fprintf(stdout,\", foreign_keys: %s\", foreign_keys);\n  fprintf(stdout,\", lineno: %d\\n\", lineno);\n  fprintf(stdout,\"htmlparams: %s\\n\", htmlparams);\n  fprintf(stdout,\"data: %s\\n\", data);\n\n#define FOREIGN_QUERY_ADD_STRING(string) \\\n  fprintf(stdout,\"foreign data2: %s count: %d, foreign_query_length: %d, strlen:%ld, string: \\\"%s\\\"\\n\", \\\n          foreign_query, count, foreign_query_length, (long int)count + strlen(string),string); \\\n  if(count + strlen(string) &lt; foreign_query_length) { \\\n    strcat(foreign_query, string); \\\n  } \\\n  count += strlen(string);\n\n  f=foreign_keys;\n  while(*f != '\\0') {\n    db_skipwhite(&amp;f);\n    if(*f==';') {\n      f++;\n      if(semicolonneeded==1) {\n        FOREIGN_QUERY_ADD_STRING(\";\")\n        semicolonneeded=0;\n        semicolonprinted=1;\n      }\n      continue;\n    }\n    db_parse_nameandvalue(sizeof(toname), toname, sizeof(fromname), fromname, &amp;f);\n    dba_app_get_parameter_id(parameterid,toname);\n    int datastat=db_get_field(data,toname,sizeof(value),value);\n    int parameterstat;\n    if((parameterstat=dbs_get_parameter(parameterid, sizeof(parametervalue), parametervalue))!=1)\n      parametervalue&#91;0]='\\0';\n    if(datastat==1) {\n      fprintf(stdout,\"toname: %s\", toname);\n      fprintf(stdout,\", fromname: %s\", fromname);\n      fprintf(stdout,\", parameterid: %s\", parameterid);\n      fprintf(stdout,\", parameter value: \\\"%s\\\"\", parametervalue);\n      fprintf(stdout,\", datastat: %d\", datastat);\n      fprintf(stdout,\", parameterstat2: %d\", parameterstat);\n      fprintf(stdout,\", name: %s\", toname);\n      fprintf(stdout,\", value: \\\"%s\\\"\", value);\n      fprintf(stdout,\", lineno: %d\", lineno);\n      fprintf(stdout,\"\\n\");\n\n      \/\/strcpy(fromname,\"jk\");                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \n      \/\/strcpy(parametervalue,\"jk\");                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 \n      if(!first) {\n        if(!semicolonprinted) {\n          FOREIGN_QUERY_ADD_STRING(\",\")\n        }\n        semicolonprinted=0;\n        FOREIGN_QUERY_ADD_STRING(\" \")\n      }\n      FOREIGN_QUERY_ADD_STRING(\"'\")\n      FOREIGN_QUERY_ADD_STRING(fromname)\n      FOREIGN_QUERY_ADD_STRING(\"'\")\n      if(*parametervalue!='\\0' || parameterstat != 1) {\n        FOREIGN_QUERY_ADD_STRING(\" \")\n        FOREIGN_QUERY_ADD_STRING(\"\\\"\")\n        FOREIGN_QUERY_ADD_STRING(parametervalue)\n        FOREIGN_QUERY_ADD_STRING(\"\\\"\")\n      }\n      first=0;\n      semicolonneeded=1;\n      fprintf(stdout,\"\\n\");\n    } \/\/ if(datastat==1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              \n  } \/\/ while(*f                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      \n  return(count);\n}\n\nint dba_app_get_foreign_query(unsigned char *foreign_keys, unsigned char *data)\n{\n  int count, first;\n\n  fprintf(stdout,\"foreign_query try\\n\");\n  count=dba_app_get_foreign_query2(dba_app_foreign_query_length, dba_app_foreign_query, foreign_keys, data) + 1;\n  fprintf(stdout,\"foreign data: \\\"%s\\\", count=%d\\n\",dba_app_foreign_query, count);\n  if(dba_app_foreign_query_length &lt; count) {\n    dba_app_foreign_query_length = count;\n    dba_app_foreign_query=realloc(dba_app_foreign_query, dba_app_foreign_query_length);\n    count=dba_app_get_foreign_query2(dba_app_foreign_query_length, dba_app_foreign_query, foreign_keys, data) + 1;\n    fprintf(stdout,\"foreign data: \\\"%s\\\", count=%d\\n\",dba_app_foreign_query, count);\n  }\n  fprintf(stdout,\"&lt;&lt;&lt; foreign query: \\\"%s\\\" >>>\\n\", dba_app_foreign_query);\n  fflush(stdout);\n}<\/code><\/pre>\n\n\n\n<p>Toiseksi viimeinen osa vierasavainten (foreign key) k\u00e4sittelyst\u00e4 dba_foreign_data. Se lukee edellisen foreign_query &#8211; funktion merkkijonon, esimerkiksi: huomaa sovellusten v\u00e4lill\u00e4 oleva puolipiste.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'tuotenumero' \"2000\", 'tuotteen hinta', 'tuotteen nimi'; 'tuotenumero' \"2000\", 'tuotteen hinta', 'tuotteen nimi';<\/code><\/pre>\n\n\n\n<p>Ja tekee siit\u00e4 t\u00e4ll\u00e4ist\u00e4: T\u00e4ss\u00e4 viimeisess\u00e4 mallissa ei ole puolipisteit\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'tuotenumero' \"2000\", 'tuotteen hinta' \"5,00\", 'tuotteen nimi' \"Tappi\", 'tuotenumero' \"2000\", 'tuotteen hinta' \"5,00\", 'tuotteen nimi' \"Tappi\"<\/code><\/pre>\n\n\n\n<p>Eli suorittaa _query listassa olevat kyselyt ja ynn\u00e4\u00e4 kyselyjen vastaukset foreign_data merkkijonoon. Sitten varsinaiseen koodiin: T\u00e4ss\u00e4 merkkijonot k\u00e4yd\u00e4\u00e4n l\u00e4pi kentt\u00e4 kent\u00e4lt\u00e4, sen voisi tehd\u00e4 nopeamminkin, mutta se j\u00e4\u00e4 my\u00f6hemm\u00e4ksi. Aliohjelma k\u00e4ytt\u00e4\u00e4 uusia dba_string_set ja dba_string_add funktioita.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int dba_app_get_foreign_data(int *foreign_data_length, unsigned char **foreign_data, unsigned char *foreign_query)\n{\n  int stat, first, first2;\n  unsigned char name&#91;32], value&#91;1024];\n  unsigned char *f, *g;\n\n  fprintf(stdout,\"Foreign data: %s\", foreign_query);\n  fprintf(stdout,\"\\n\");\n  f=foreign_query;\n\n  dba_string_set(&amp;dba_app_foreign_data_length, &amp;dba_app_foreign_data, \"\");\n  first2=1;\n\n  while(*f != '\\0') {\n    first = 1;\n    dba_string_set(&amp;dba_app_foreign_data_query_length, &amp;dba_app_foreign_data_query, \"\");\n    while(*f!=';' &amp;&amp; *f!='\\0')  {\n      db_skipwhite(&amp;f);\n      stat=db_parse_nameandvalue(sizeof(name), name,\n          sizeof(value), value, &amp;f);\n      fprintf(stdout,\"foreign data query: name: %s\", name);\n      fprintf(stdout,\", value: %s\", value);\n      fprintf(stdout,\", stat: %d\\n\", stat);\n#define FOREIGN_DATA_ADD_QUERY(string) \\\n      dba_string_add(&amp;dba_app_foreign_data_query_length, &amp;dba_app_foreign_data_query, string);\n      if(!first) {\n        FOREIGN_DATA_ADD_QUERY(\", \");\n      }\n      FOREIGN_DATA_ADD_QUERY(\"'\");\n      FOREIGN_DATA_ADD_QUERY(name);\n      FOREIGN_DATA_ADD_QUERY(\"'\");\n      if(stat>1) {\n        FOREIGN_DATA_ADD_QUERY(\" \");\n        FOREIGN_DATA_ADD_QUERY(\"\\\"\");\n        FOREIGN_DATA_ADD_QUERY(value);\n        FOREIGN_DATA_ADD_QUERY(\"\\\"\");\n      }\n      first=0;\n    } \/\/ while(*f!=';'                                                                                                                                                                                                                                                                                                                                                         \n    if(*f==';')\n      f++;\n    fprintf(stdout,\"queryall: %s\\n\", dba_app_foreign_data_query);\n\n    struct query *q;\n    struct data *d;\n\n    first = 1;\n    q = dba_query(dba_app_foreign_data_query);\n    d = dba_query_get_first(q);\n    \/\/ Exactly one record\n    if(d!=NULL &amp;&amp; d->next_data==NULL) {                                                                                                                                                                                                                                                                                                                  \n      g=d->data;\n      while(*g!='\\0') {\n        db_skipwhite(&amp;g);\n        stat=db_parse_nameandvalue(sizeof(name), name,\n            sizeof(value), value, &amp;g);\n        fprintf(stdout,\"\\nforeign data result: name: %s\", name);\n        fprintf(stdout,\", value: %s\", value);\n        fprintf(stdout,\", stat: %d\\n\", stat);\n#define FOREIGN_DATA_ADD_DATA(string) \\\n        dba_string_add(&amp;dba_app_foreign_data_length, &amp;dba_app_foreign_data, string);\n        if(!first2) {\n          FOREIGN_DATA_ADD_DATA(\", \");\n        }\n        FOREIGN_DATA_ADD_DATA(\"'\");\n        FOREIGN_DATA_ADD_DATA(name);\n        FOREIGN_DATA_ADD_DATA(\"'\");\n        if(stat>1) {\n          FOREIGN_DATA_ADD_DATA(\" \");\n          FOREIGN_DATA_ADD_DATA(\"\\\"\");\n          FOREIGN_DATA_ADD_DATA(value);\n          FOREIGN_DATA_ADD_DATA(\"\\\"\");\n        }\n        fprintf(stdout,\"&lt;&lt;&lt; foreign data result added: \\\"%s\\\" >>>\\n\", dba_app_foreign_data);\n\n        first2=0;\n      } \/\/ while(*g!='\\0') {                                                                                                                                                                                                                                                                                                                                                   \n    } \/\/ if(d!=NULL                                                                                                                                                                                                                                                                                                                                                            \n  } \/\/ while(*f != '\\0')                                                                                                                                                                                                                                                                                                                                                       \n  fprintf(stdout,\"&lt;&lt;&lt; foreign data results: \\\"%s\\\" >>>\\n\", dba_app_foreign_data);\n}<\/code><\/pre>\n\n\n\n<p>Sitten sovelluksen nimen k\u00e4\u00e4nn\u00f6s:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_app()\n{\n  struct query *qlanguage;\n  struct data *dlanguage;\n  unsigned char apptext&#91;64];\n\n  sprintf(query,\"'app' \\\"%s\\\", 'language'\"\n      \" \\\"%s\\\", 'apptext'\", app,\n      htmllanguage);\n  qlanguage=db_query(query);\n  if((dlanguage = db_query_get_first(\n      qlanguage))!=NULL) {\n    db_data_get_field(dlanguage, \"apptext\",\n        sizeof(apptext),apptext);\n  } else {\n    sprintf(apptext,\"%s\", appname);\n  }\n  fprintf(stdout,\"\\n\");\n\n  dbs_html_printf(apptext);\n}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Sarakkeen nimen k\u00e4\u00e4nn\u00f6s:<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_memberid()\n{\n  struct query *qlanguage;\n  struct data *dlanguage;\n  unsigned char membertext&#91;64];\n\n  sprintf(query,\"'memberid' \\\"%s\\\", 'language' \\\"%s\\\", 'membertext'\", memberid, htmllanguage);\n  qlanguage=dba_query(query);\n  if((dlanguage = dba_query_get_first(qlanguage))!=NULL) {\n    db_data_get_field(dlanguage, \"membertext\",\n                      sizeof(membertext),membertext);\n    fprintf(stdout,\"query: %s\",query);\n    fprintf(stdout,\", membertext: %s\",membertext);\n  } else {\n    sprintf(query,\"'word' \\\"%s\\\", 'language' \\\"%s\\\", 'translation'\", memberid, htmllanguage);\n    qlanguage=dba_query(query);\n    if((dlanguage = dba_query_get_first(qlanguage))!=NULL) {\n      db_data_get_field(dlanguage, \"translation\",\n                        sizeof(membertext),membertext);\n      fprintf(stdout,\"query: %s\",query);\n      fprintf(stdout,\", membertext: %s\",membertext);\n    } else {\n      sprintf(membertext,\"%s\", memberid);\n    }\n  }\n  fprintf(stdout,\"\\n\");\n\n  dbs_html_printf(membertext);\n}\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>T\u00e4ss\u00e4 pidempi rutiini joka tekee kaiken varsinaiseen kent\u00e4n arvoon liittyv\u00e4n:<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app_value()\n{\n  struct query *qmemberiddata;\n  struct data *dmemberiddata;\n  unsigned char lengths&#91;10];\n  int length;\n\n  \/\/ Find length of field                                                                                                                                                                                                                                                                                         \n  length = 10;\n  sprintf(query,\"'memberid' \\\"%s\\\",\n      'length'\", memberid);\n  qmemberiddata = db_query(query);\n  if((dmemberiddata =\n      db_query_get_first(qmemberiddata))\n     != NULL) {\n    if(db_data_get_field(dmemberiddata,\n        \"length\", sizeof(lengths),\n          lengths)) {\n      length=atoi(lengths);\n    }\n  }\n\n  \/\/ Find mode of field                                                                                                                                                                                                                                                                                           \n  if(!strcmp(func, \"Reset\"))\n    value&#91;0] = '\\0';\n  else {\n    \/\/ get field value                                                                                                                                                                                                                                                                                            \n    char tempmemberid&#91;64];\n    sprintf(tempmemberid, \"%s-%d\",\n        memberid, lineno);\n\n    if(dbs_get_parameter(tempmemberid,\n        sizeof(value),\n        value)==0) {\n      db_data_get_field(ddata, memberid,\n          sizeof(value), value);\n    }\n  }\n\n  char mode&#91;10];\n  strcpy(mode, \"Required\");\n  sprintf(query,\"'memberid' \\\"%s\\\",\"\n      \" 'mode'\", memberid);\n  qmemberiddata = db_query(query);\n  if((dmemberiddata = db_query_get_first(\n      qmemberiddata))\n     != NULL) {\n    if(db_data_get_field(dmemberiddata,\n        \"mode\", sizeof(mode), mode)) {\n      length=atoi(lengths);\n    }\n  }\n\n  int errors = 0;\n\n  \/\/ Do field checks                                                                                                                                                                                                                                                                                              \n  if(!strcmp(mode, \"Required\") &amp;&amp;\n      value&#91;0] == '\\0') {\n    errors++;\n    formerrors++;\n  }\n\n  \/\/ print field input                                                                                                                                                                                                                                                                                            \n  sprintf(htmltext,\"&lt;input type=\\\"text\\\"\"\n      \" value=\\\"%s\\\" name=\\\"%s-%d\\\"\"\n      \" size=\\\"%d\\\"\", value, memberid,\n      lineno, length);\n  if(strcmp(htmlmode, \"Change\"))\n    strcat(htmltext, \" readonly\");\n  \/\/ red if errors                                                                                                                                                                                                                                                                                                \n  if(errors != 0 &amp;&amp; (\n    !strcmp(func, \"Check\") ||\n    !strcmp(func,\"Save\")))\n      strcat(htmltext,\" style=\\\"border-color:red;\\\"\");\n  strcat(htmltext,\">&lt;br>\");\n  dbs_html_printf(htmltext);\n}<\/code><\/pre>\n\n\n\n<p>Ja varsinainen sovellusp\u00e4\u00e4ohjelma:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_app()\n{\n  int first;\n  unsigned char *p;\n\n  DB2_EVENTS_START(302)\n\n  qheadermemberid = NULL; \/\/ save for dump                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             \n  qheaderdata = NULL;\n  qlinesmemberid = NULL;\n  qlinesdata = NULL;\n\n  if(dbs_get_parameter(\"func-0\", sizeof(func), func)==1)\n    fprintf(stdout,\"FUNC:\\\"%s\\\"\\n\",func);\n\n  if(!strcmp(func, \"Display\") ||\n     !strcmp(func, \"Change\"))\n    strcpy(htmlmode, func);\n\n  dba_renew_cookie();\n\n  p = htmlparams;\n  dbs_html_get_string(htmlstring, &amp;p, 1024);\n  fprintf(stdout,\"htmlstring: \\\"%s\\\"\", htmlstring);\n\n  if(dbs_get_parameter(\"app-0\", sizeof(app), app)==0)\n    sprintf(app,\"asiakas\");\n  else\n    fprintf(stdout,\"APP:\\\"%s\\\"\\n\", app);\n\n  lineno = 0;\n\n  sprintf(htmltext,\"&lt;form action=\\\"app\\\" method=\\\"post\\\">\");\n  dbs_html_printf(htmltext);\n  \/\/ app selection                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     \n  sprintf(htmltext,\"&lt;input type=\\\"text\\\" value=\\\"%s\\\" name=\\\"app-%d\\\" size=\\\"10\\\">\", app, lineno);\n  dbs_html_printf(htmltext);\n  \/\/ print buttons                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     \n  dba_app_buttons();\n  dbs_html_printf(\"&lt;br>\");\n  fprintf(stdout,\"htmlparams:\\\"%s\\\"\\n\", htmlparams);\n  fprintf(stdout,\"app: %s\\n\", app);\n\n  sprintf(query,\"'app' \\\"%s\\\", 'appname'\", app);\n  qappname=dba_query(query);\n  dappname=dba_query_get_first(qappname);\n  fprintf(stdout,\"dappname->data=%s\\n\", dappname->data);\n  fflush(stdout);\n  if(db_data_get_field(dappname,\"appname\",\n      sizeof(appname), appname)) {\n    dbs_html_printf(\"&lt;h1>\");\n    dba_app_app();\n    dbs_html_printf(\"&lt;\/h1>\");\n    fprintf(stdout,\"app=%s\", app);\n    fprintf(stdout,\"\\n\");\n  }\n\n  int count;\n\n  fprintf(stdout,\"foreign_keys try\\n\");\n  count=dba_app_get_foreign_keys(dba_app_foreign_keys_length, dba_app_foreign_keys, app) + 1;\n  fprintf(stdout,\"str: %s, count=%d\\n\",dba_app_foreign_keys, dba_app_foreign_keys_length);\n  if(count>dba_app_foreign_keys_length) {\n    dba_app_foreign_keys_length = count;\n    dba_app_foreign_keys=realloc(dba_app_foreign_keys, dba_app_foreign_keys_length);\n    count=dba_app_get_foreign_keys(dba_app_foreign_keys_length, dba_app_foreign_keys,app) + 1;\n    fprintf(stdout,\"str: %s, count=%d\\n\",dba_app_foreign_keys, dba_app_foreign_keys_length);\n  }\n  fprintf(stdout,\"Foreign keys: %s\", dba_app_foreign_keys);\n  dbs_html_buf_printf(HTML_FOREIGN_BUFFER,\n    \"Foreign keys: %s&lt;br>\", dba_app_foreign_keys);\n  formerrors=0;\n\n  sprintf(query,\"'app' \\\"%s\\\", 'chapter'\", app);\n  qchapter=dba_query(query);\n  dchapter=dba_query_get_first(qchapter);\n\n  while(dchapter != NULL) {\n    if(db_data_get_field(dchapter, \"chapter\",\n        sizeof(chapter),chapter)) {\n      fprintf(stdout,\"chapter=%s\", chapter);\n      fprintf(stdout,\"\\n\");\n    }\n    sprintf(query,\"'app' \\\"%s\\\", 'chapter' \\\"%s\\\", 'sort', 'memberid'\", app, chapter);\n    qmemberid=dba_query(query);\n    dmemberid=dba_query_get_first(qmemberid);\n\n    \/\/ generate query for data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         \n    *query = '\\0';\n    first = 1;\n    while(dmemberid != NULL) {\n      db_data_get_field(dmemberid,\"memberid\",\n          sizeof(memberid), memberid);\n      if(!first)\n        strcat(query,\", \");\n      strcat(query,\"'\");\n      strcat(query,memberid);\n      strcat(query,\"'\");\n      first=0;\n\n      dmemberid=dba_data_get_next(dmemberid);\n    }\n\n    qdata=dba_query(query);\n    ddata=dba_query_get_first(qdata);\n    while(ddata != NULL) {\n      fprintf(stdout,\"data: %s\\n\", ddata->data);\n      ddata=dba_data_get_next(ddata);\n    }\n\n    if(!strcmp(chapter,\"header\")) {\n      lineno++;\n\n      dbs_html_printf(\"&lt;table border=\\\"0\\\">\");\n\n      ddata=dba_query_get_first(qdata);\n\n      \/\/dba_app_foreign_keys&#91;0]='\\0';                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  \n      \/\/if(ddata!=NULL &amp;&amp; ddata->data!=NULL)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           \n      dba_app_get_foreign_query(\n        dba_app_foreign_keys, ddata->data);\n      dbs_html_buf_printf(HTML_FOREIGN_BUFFER,\n        \"line %d query: %s&lt;br>\", lineno, dba_app_foreign_query);\n      dba_app_get_foreign_data(&amp;dba_app_foreign_data_length,\n        &amp;dba_app_foreign_data,dba_app_foreign_query);\n      dbs_html_buf_printf(HTML_FOREIGN_BUFFER,\n        \"line %d data:  %s&lt;br>\", lineno, dba_app_foreign_data);\n\n      qheadermemberid = qmemberid; \/\/ save for dump                                                                                           \n      qheaderdata = qdata;\n\n      qheadermemberid = qmemberid; \/\/ save for dump                                                                                                                                                                                                                                                                                                                                                                                                                        \n      qheaderdata = qdata;\n\n      dmemberid = dba_query_get_first(qmemberid);\n      while(dmemberid != NULL) {\n        db_data_get_field(dmemberid, \"memberid\",\n            sizeof(memberid), memberid);\n\n        dbs_html_printf(\"&lt;tr>\");\n\n        dbs_html_printf(\"&lt;td>\");\n        dba_app_memberid();\n        dbs_html_printf(\"&lt;\/td>\");\n\n        dbs_html_printf(\"&lt;td>\");\n        dba_app_value();\n        dbs_html_printf(\"&lt;\/td>\");\n\n        dbs_html_printf(\"&lt;\/tr>\");\n        dmemberid=dba_data_get_next(dmemberid);\n      }\n      dbs_html_printf(\"&lt;\/table>\");\n\n    } else if(!strcmp(chapter,\"lines\")) {\n\n      dbs_html_printf(\"&lt;table border=\\\"0\\\">\");\n      dbs_html_printf(\"&lt;tr>\");\n\n      dmemberid=dba_query_get_first(qmemberid);\n\n      qlinesmemberid = qmemberid; \/\/ save for dump                                                                                                                                                                                                                                                                                                                                                                                                                         \n      qlinesdata = qdata;\n\n      while(dmemberid != NULL) {\n        db_data_get_field(dmemberid, \"memberid\",\n            sizeof(memberid), memberid);\n        dbs_html_printf(\"&lt;td>\");\n        dba_app_memberid();\n        dbs_html_printf(\"&lt;\/td>\");\n        dmemberid=dba_data_get_next(dmemberid);\n      }\n      dbs_html_printf(\"&lt;\/tr>\");\n      ddata = dba_query_get_first(qdata);\n\n      while(ddata != NULL) {\n        lineno++;\n\n\tdba_app_get_foreign_query(dba_app_foreign_keys,\n          ddata->data);\n        dbs_html_buf_printf(HTML_FOREIGN_BUFFER,\n          \"line %d query: %s&lt;br>\", lineno,\n          dba_app_foreign_query);\n        dba_app_get_foreign_data(&amp;dba_app_foreign_data_length,\n          &amp;dba_app_foreign_data, dba_app_foreign_query);\n\tdbs_html_buf_printf(HTML_FOREIGN_BUFFER,\n          \"line %d data:  %s&lt;br>\", lineno,\n          dba_app_foreign_data);\n        \n        fprintf(stdout,\"%s\\n\", ddata->data);\n        dbs_html_printf(\"&lt;tr>\");\n        dmemberid=dba_query_get_first(qmemberid);\n        while(dmemberid != NULL) {\n          db_data_get_field(dmemberid, \"memberid\",\n              sizeof(memberid), memberid);\n          dbs_html_printf(\"&lt;td>\");\n          dba_app_value();\n          dbs_html_printf(\"&lt;\/td>\");\n          dmemberid=dba_data_get_next(dmemberid);\n        }\n        dbs_html_printf(\"&lt;\/tr>\");\n        ddata=dba_data_get_next(ddata);\n      }\n      dbs_html_printf(\"&lt;\/table>\");\n    } \/\/ end of if(!strcmp(chapter,\"lines\"))                                                                                                                                                                                                                                                                                                                                                                                                                               \n    dchapter=dba_data_get_next(dchapter);\n  } \/\/ end of while(dchapter!=NULL) {                                                                                                                                                                                                                                                                                                                                                                                                                                      \n\n  if(!strcmp(func, \"Save\") &amp;&amp; formerrors == 0) {\n    dba_app_save();\n  }\n\n  dbs_html_printf(\"&lt;p>application: %s(%s)&lt;\/p>\", app, appname);\n  dbs_html_printf(\"&lt;p>htmlparams: %s&lt;\/p>\", htmlparams);\n  dbs_html_printf(\"&lt;p>htmlstring: %s&lt;\/p>\", htmlstring);\n  dbs_html_printf(\"&lt;p>foreign keys: %s&lt;\/p>\", dba_app_foreign_keys);\n#ifdef OLD\n  dbs_html_printf(\"&lt;code>\");\n  dbs_dump_query(qappname);\n  dbs_dump_query(qchapter);\n  if(qheadermemberid != NULL)\n    dbs_dump_query(qheadermemberid);\n  if(qlinesmemberid != NULL)\n    dbs_dump_query(qlinesmemberid);\n  if(qheaderdata != NULL)\n    dbs_dump_query(qheaderdata);\n  if(qlinesdata != NULL)\n    dbs_dump_query(qlinesdata);\n  dbs_html_printf(\"&lt;\/code>\");\n#endif\n  dbs_html_printf(\"&lt;\/form>\");\n  dbs_html_printf(\"&lt;br>&lt;br>%s,  sha256(%s)&lt;br>&lt;br>\", programname,\n      htmldigest);\n\n  DB2_EVENTS_END(303)\n}\n<\/code><\/pre>\n\n\n\n<p>Seuraavassa html otsakkeen lukuun k\u00e4ytettyj\u00e4 funktioita: Ensiksi kuitenkin malli html otsakkeista: ensimm\u00e4iselt\u00e4 tietueelta luetaan html-method ja path. Cookie-rivilt\u00e4 luetaan mode ja session id. Parametrit rivilt\u00e4 luetaan html lomakkeen parametrit.<\/p>\n\n\n\n<p>Metodirivi on tiedoston ensimm\u00e4inen rivi ja sill\u00e4 on v\u00e4lily\u00f6nnein eroteltuna method, path ja html-type. Cookie rivi alkaa &#8220;Cookie:&#8221; tekstill\u00e4.<\/p>\n\n\n\n<p>Parametririvi\u00e4 edelt\u00e4\u00e4 nelj\u00e4 merkki\u00e4, &#8220;\\r\\n\\r\\n&#8221; merkkijono.<\/p>\n\n\n\n<p>Content-Length kentt\u00e4 sis\u00e4lt\u00e4\u00e4 &#8220;payload&#8221; kent\u00e4n pituuden eli t\u00e4ss\u00e4 tapauksessa parametrien pituuden. Palvelimen l\u00e4hett\u00e4ess\u00e4 html-sivun Content-Length kentt\u00e4 sis\u00e4lt\u00e4\u00e4 html tekstin pituuden.<\/p>\n\n\n\n<p>Otsakkeessa rivien v\u00e4liss\u00e4 on aina &#8216;\\r\\n&#8217;.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>POST \/app HTTP\/1.1^M\nHost: 192.168.1.14:5004^M\nUser-Agent: Mozilla\/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko\/20100101 Firefox\/82.0^M\nAccept: text\/html,application\/xhtml+xml,application\/xml;q=0.9,image\/webp,*\/*;q=0.8^M\nAccept-Language: en-US,en;q=0.5^M\nAccept-Encoding: gzip, deflate, br^M\nContent-Type: application\/x-www-form-urlencoded^M\nContent-Length: 286^M\nOrigin: https:\/\/192.168.1.14:5004^M\nConnection: keep-alive^M\nReferer: https:\/\/192.168.1.14:5004\/app^M\nCookie: sessionid=WMozuNOpY6mpXVb8peyl4yvSKagCDqNN; mode=Change; language=fi^M\nUpgrade-Insecure-Requests: 1^M\n^M\napp-0=tilaus&amp;func-0=Submit&amp;tilausnumero-1=3000&amp;tilauksen+asiakasnumero-1=1000&amp;tilauksen+asiakkaan+nimi-1=Firma+1&amp;tilausnu\\\nmero-2=3000&amp;tilauksen+rivinumero-2=10&amp;tilauksen+tuotenumero-2=2000&amp;tilauksen+tuotteen+nimi-2=Tappi&amp;tilauksen+tuotteen+hin\\\nta-2=5%2C00&amp;tilattu+m%C3%A4%C3%A4r%C3%A4-2=1\"\n<\/code><\/pre>\n\n\n\n<p>Dbs_html_get_request_line:lla haetaan otsakkeen rivi, jonka alku sis\u00e4lt\u00e4\u00e4 helutun merkkijonon.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned char *dbs_html_get_request_line(unsigned char *name)\n{\n  unsigned char *p;\n\n  p = htmlin;\n  while(*p != '\\0') {\n    if(!strncmp(p, \"\\r\\n\", 2)) {\n      p += 2;\n      if(!strncmp(p, name, strlen(name))) {\n        p += strlen(name);\n        if(*p == ':') {\n          p++;\n          while(isblank(*p))\n            p++;\n          return(p);\n        }\n      }\n    }\n    p++;\n  }\n  return(NULL);\n}<\/code><\/pre>\n\n\n\n<p>dbs_html_get_request_line_num on numeerinen versio edellisest\u00e4: t\u00e4ll\u00e4 luetaan ainakin Content-Length kent\u00e4n arvo.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_html_get_request_line_num(unsigned char *name, int lenbuffer, unsigned char *buffer)\n{\n  int len;\n  unsigned char *n, *p;\n\n  if((p = dbs_html_get_request_line(name)) == NULL)\n    sprintf(buffer, \"0\");\n  else {\n    len = 0;\n    n = buffer;\n    while(isdigit(*p)) {\n      if(++len &lt; lenbuffer)\n        *n++ = *p;\n      p++;\n    }\n    *n = '\\0';\n  }\n}<\/code><\/pre>\n\n\n\n<p>Dbs_get_cookie funktiolla luetaan cookiet, jotka l\u00f6ytyv\u00e4t otsakkeen &#8220;Cookie:&#8221; merkkijonolla alkavalta rivilt\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_get_cookie(unsigned char *name, int valuelen, unsigned char *value)\n{\n  int len;\n  unsigned char *p,*n;\n\n  if((p = dbs_html_get_request_line(\"Cookie\")) == NULL) {\n    return;\n  }\n\n  while(*p!='\\n' &amp;&amp; *p!='\\0') {\n    while(isblank(*p))\n      p++;\n    if(!strncmp(p, name, strlen(name))) {\n      p += strlen(name);\n      while(isblank(*p))\n        p++;\n      if(*p == '=')\n        p++;\n      while(isblank(*p))\n        p++;\n      n = value;\n      len = 0;\n      while(*p != ';' &amp;&amp; isprint(*p)) {\n        if(++len &lt; sizeof(htmlsessionid))\n          *n++ = *p;\n        p++;\n      }\n      *n = '\\0';\n      return;\n    }\n    while(*p != ';' &amp;&amp;\n        *p != '\\n' &amp;&amp;\n        *p != '\\0')\n      p++;\n    if(*p == ';')\n      p++;\n    while(isblank(*p))\n      p++;\n  }\n}<\/code><\/pre>\n\n\n\n<p>Aliohjelma kopioi v\u00e4lily\u00f6nnill\u00e4\/v\u00e4lily\u00f6nneill\u00e4 erotetun merkkijonon buf merkkijonoon.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_html_parse_string(int buflen, char *buf, unsigned char **p2)\n{\n  int len;\n  unsigned char *p, *n;\n\n  p = *p2;\n  len = 0;\n  while(!isblank(*p) &amp;&amp; *p!='\\n' &amp;&amp; *p!='\\r') {\n    if(++len &lt; buflen) {\n      *buf++ = *p;\n    }\n    p++;\n  }\n  *buf = '\\0';\n\n  while(isblank(*p))\n    p++;\n\n  *p2 = p;\n}<\/code><\/pre>\n\n\n\n<p>Seuraava rutiini kopioi tiedostonimen tiedostopolusta buf merkkijonomuuttujaan. Tiedostopolkuhan alkaa viimeisen kauttaviivan j\u00e4lkeisest\u00e4 merkist\u00e4.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_html_parse_filename(int buflen, char *buf, unsigned char *p)\n{\n  int len;\n  unsigned char *n;\n\n  len = 0;\n  n = buf;\n\n  while(*p != '\\0') {\n    if(*p == '\/') {\n      n = buf;\n      len = 0;\n    } else {\n      if(++len &lt; buflen)\n        *n++ = *p;\n    }\n    p++;\n  }\n  *n = '\\0';\n}<\/code><\/pre>\n\n\n\n<p>Seuraava rutiini palauttaa osoitteen html-otsakkeen parameters kentt\u00e4\u00e4n. Se l\u00f6ytyy &#8220;\\r\\n\\r\\n&#8221; merkkijonon j\u00e4lkeen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>unsigned char *dbs_html_get_params()\n{\n  unsigned char *p = htmlin;\n\n  while(*p != '\\0') {\n    if(!strncmp(p, \"\\r\\n\\r\\n\", 4)) {\n      p += 4;\n      break;\n    }\n    p++;\n  }\n  return(p);\n}<\/code><\/pre>\n\n\n\n<p>T\u00e4m\u00e4 on p\u00e4\u00e4ohjelma, joka hakee arvot html-alkuisille muuttujilla ja kutsuu dba_logon rutiinia tai dba_app rutiinia.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dba_loop()\n{\n  unsigned char *p;\n\n  p=htmlin;\n  dbs_html_parse_string(sizeof(htmlmethod), htmlmethod,&amp;p);\n  fprintf(stdout,\"htmlmethod: \\\"%s\\\"\", htmlmethod);\n  fflush(stdout);\n\n  dbs_html_parse_string(sizeof(htmlpath), htmlpath, &amp;p);\n  fprintf(stdout,\", htmlpath: \\\"%s\\\"\", htmlpath);\n  fflush(stdout);\n\n  dbs_html_parse_string(sizeof(htmlversion), htmlversion, &amp;p);\n  fprintf(stdout,\", htmlversion: \\\"%s\\\"\", htmlversion);\n  fflush(stdout);\n\n  dbs_html_parse_filename(sizeof(htmlfilename), htmlfilename, htmlpath);\n  fprintf(stdout,\", htmlfilename: \\\"%s\\\"\", htmlfilename);\n  fflush(stdout);\n\n  htmlparams=dbs_html_get_params();\n  fprintf(stdout,\", htmlparamslen: \\\"%ld\\\"\", strlen(htmlparams));\n\n  strcpy(htmlmode,\"Display\");\n  htmlsessionid&#91;0]='\\0';\n\n  dbs_get_cookie(\"sessionid\", sizeof(htmlsessionid), htmlsessionid);\n  fprintf(stdout,\", htmlsessionid: \\\"%s\\\"\", htmlsessionid);\n  fflush(stdout);\n\n  dbs_get_cookie(\"mode\", sizeof(htmlmode), htmlmode);\n  fprintf(stdout,\", htmlmode: \\\"%s\\\"\\n\", htmlmode);\n  fflush(stdout);\n\n  dbs_get_cookie(\"language\", sizeof(htmllanguage), htmllanguage);\n  fprintf(stdout,\", htmllanguage: \\\"%s\\\"\\n\", htmllanguage);\n  fflush(stdout);\n\n  for(int c=0;c&lt;HTML_BUFFERS;c++) {\n    dbs_html_set(c);\n    dbs_html_clear();\n  }\n\n  dbs_html_set(HTML_HEADER_BUFFER);\n\n  dbs_html_printf(\"HTTP\/1.0 200 OK\\r\\n\");\n  dbs_html_printf(\"Location: \\r\\n\");\n  dbs_html_printf(\"Server: %s\\r\\n\", programname);\n\n  \/\/ query statements and data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    \n  dbs_html_set(HTML_FOREIGN_BUFFER);\n  dbs_html_printf(\"&lt;h2>Foreign keys&lt;\/h2>\");\n  dbs_html_printf(\"&lt;code>\");\n\n  dbs_html_set(HTML_QUERY_BUFFER);\n  dbs_html_printf(\"&lt;h2>Queries&lt;\/h2>\");\n  dbs_html_printf(\"&lt;code>\");\n\n  dbs_html_set(HTML_PAYLOAD_BUFFER);\n\n  dbs_html_printf(\"\\n&lt;!doctype html>\\r\\n\");\n  dbs_html_printf(\"&lt;html lang=\\\"fi\\\">\");\n\n  dbs_html_printf(\"&lt;head>\");\n  dbs_html_printf(\"&lt;meta charset=\\\"utf-8\\\">\");\n  dbs_html_printf(\"&lt;title>dbs&lt;\/title>\");\n  dbs_html_printf(\"&lt;meta name=\\\"author\\\" content=\\\"Jari Kuivaniemi\\\">\");\n  dbs_html_printf(\"&lt;meta name=\\\"copyright\\\" content=\\\"Jari Kuivaniemi\\\">\");\n  dbs_html_printf(\"&lt;\/head>\");\n\n  dbs_html_printf(\"&lt;body>\");\n\n  if(htmlsessionid&#91;0]=='\\0') {\n    strcpy(htmllanguage, \"fi\");\n    dba_logon();\n  } else if(!strcmp(htmlfilename, \"logon\")) {\n    htmlsessionid&#91;0] = '\\0';\n    strcpy(htmllanguage, \"fi\");\n    dba_logon();\n  }\n\n  if(htmlsessionid&#91;0] != '\\0') {\n    dba_app();\n  }\n#ifdef TEST\n  dbs_html_printf(\"&lt;h1>Hello, world! \u00e5\u00e4\u00f6&lt;\/h1>\");\n  dbs_html_printf(\"&lt;form action=\\\"httpstest2\\\" method=\\\"post\\\">\");\n  dbs_html_printf(\"&lt;input type=\\\"submit\\\" name=\\\"func\\\" value=\\\"Submit\\\">\");\n  dbs_html_printf(\"&lt;input type=\\\"text\\\" value=\\\"1234\u00e5\u00e4\u00f6\\\" name=\\\"sovellus\u00e4\\\" size=\\\"10\\\">\");\n  dbs_html_printf(\"&lt;p>htmlparams: %s&lt;\/p>\",htmlparams);\n  dbs_html_printf(\"&lt;p>htmlstring: %s&lt;\/p>\",htmlstring);\n  dbs_html_printf(\"&lt;\/form>\");\n#endif\n\n  dbs_html_printf(\"&lt;br>&lt;br>%s,  sha256(%s)&lt;br>&lt;br>\", programname,\n      htmldigest);\n  dbs_html_printf(\"&lt;\/body>\");\n\n  \/\/dbs_html_printf(\"&lt;\/html>\");                                                                                                                                                                                       \n\n  \/\/ query statements and data                                                                                                                                                                                        \n  dbs_html_set(HTML_FOREIGN_BUFFER);\n  dbs_html_printf(\"&lt;\/code>\");\n\n  dbs_html_set(HTML_QUERY_BUFFER);\n  dbs_html_printf(\"&lt;\/code>\");\n\n  dbs_html_set(HTML_LAST_BUFFER);\n  dbs_html_clear();\n  dbs_html_printf(\"&lt;\/html>\");\n\n  dbs_html_set(HTML_HEADER_BUFFER);\n\n  time_t now;\n  unsigned char timebuf&#91;128];\n\n  now = time(NULL);\n  strftime(timebuf, sizeof(timebuf), HTMLTIMEFORMAT, gmtime(&amp;now));\n  dbs_html_printf(\"Date: %s\\r\\n\", timebuf);\n\n  int len;\n  len=strlen(dbs_html&#91;1]) + strlen(dbs_html&#91;2]) +\n      strlen(dbs_html&#91;3]) + strlen(dbs_html&#91;4]);\n  dbs_html_printf(\"Content-Length: %d\", len);\n  dbs_html_printf(\"\\r\\n\\r\\n\");\n\n  fprintf(stdout,\"Header_ %s\\n\",dbs_html&#91;0]);\n}\n\nvoid dba_main(int argc, char *argv&#91;])\n{\n  int c, translate;\n\n  translate = 0;\n  for(c=0; c&lt;argc; c++) {\n    if(!strcmp(argv&#91;c], \"--translate\"))\n      translate=1;\n  }\n  if(translate) {\n    dba_app_translations();\n    exit(0);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Seuraavalla rutiinilla lasketaan suoritettavalle tiedostolle SHA256 tiiviste. Tiiviste l\u00f6ytyy kaikkien sivujen alalaidasta.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void dbs_file_digest(char *filename,unsigned char *hash)\n{\n  int c;\n  unsigned char buffer&#91;1024];\n  FILE *fp1;\n  HashCtx ctx;\n\n  HashInit(&amp;ctx);\n  if((fp1 = fopen(filename, \"rb\")) != NULL) {\n    while((c = fread(buffer, 1, sizeof(buffer), fp1)) > 0)\n      HashUpdate(&amp;ctx, buffer, c);\n    fclose(fp1);\n  }\n  HashFinal(hash, &amp;ctx);\n}<\/code><\/pre>\n\n\n\n<p>Seuraavat kappaleet toteuttavat http-palvelinohjelman, https-palvelinohjelman ja p\u00e4\u00e4ohjelman. P\u00e4\u00e4ohjelma kutsuu komentoriviparametrien perusteella jompaa kumpaa, jo(t)ka taas kutsuu dba_main rutiinia.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;errno.h>\n#include &lt;openssl\/ssl.h>\n#include &lt;sys\/types.h>\n#include &lt;sys\/socket.h>\n#include &lt;signal.h>\n#include &lt;netinet\/in.h>\n#include &lt;arpa\/inet.h>\n\/* See: http:\/\/h41379.www4.hpe.com\/doc\/83final\/ba554_90007\/ch04s03.html *\/\n\n#include &lt;netdb.h>\n\n#define DEFAULT_PORT \"5004\"\n#define backlog 5\n\nint s,news;\nchar *cert_file = \"cert.pem\";\nchar *privatekey_file = \"key.pem\";\n\nchar *myport=NULL;<\/code><\/pre>\n\n\n\n<p>Seuraava koodikappale sis\u00e4lt\u00e4\u00e4 http_server rutiinin ja https_server rutiinin yhteiset osat:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int server_getaddrinfo(unsigned char *myport, struct addrinfo **res)\n{\n  int status;\n  struct addrinfo hints;\n\n  memset(&amp;hints, 0, sizeof(hints));\n  hints.ai_family = AF_UNSPEC;\n  hints.ai_socktype = SOCK_STREAM;\n  hints.ai_flags = AI_PASSIVE;\n\n  if ((status = getaddrinfo(NULL, myport, &amp;hints, res)) != 0) {\n    fprintf(stderr, \"\\n%s: getaddrinfo error: %s\",\n            procname, gai_strerror(status));\n    fprintf(stderr, \", error code %d\\n\", status);\n    fflush(stderr);\n  }\n  return(status);\n}\n\nint server_socket(struct addrinfo *res)\n{\n  int s;\n\n  if((s = socket(res->ai_family, res->ai_socktype,\n      res->ai_protocol))==-1) {\n    fprintf(stderr, \"%s: socket(), error: %d\\n\",\n        procname, errno);\n    perror(\"socket\");\n    fflush(stderr);\n  }\n\n  return(s);\n}\n\nvoid server_bind(int s, struct addrinfo *res)\n{\n  if(bind(s, res->ai_addr, res->ai_addrlen)==-1) {\n    if(errno == 98) {\n      fprintf(stdout,\"%s cannot bind, waiting to bind\",\n          procname);\n      fflush(stdout);\n      while(errno == 98) {\n        sleep(10);\n        fprintf(stdout,\".\");\n        fflush(stdout);\n        bind(s, res->ai_addr, res->ai_addrlen);\n      }\n      fprintf(stdout,\"bind done!\\n\");\n      fflush(stdout);\n    } else {\n      fprintf(stderr,\"\\n%s: cannot bind(), error: %d\\n\",'\n          procname, errno);\n      perror(\"bind\");\n      fflush(stderr);\n    }\n  }\n}\n\nint server_listen(int s)\n{\n  int listenfd;\n\n  if((listenfd=listen(s,backlog))==-1) {\n    fprintf(stderr,\"\\n%s: cannot listen()\\n\", procname);\n    perror(\"listen\");\n    fflush(stderr);\n  }\n\n  return(listenfd);\n}\n\nvoid server_close(int s)\n{\n  if(close(s)==-1) {\n    fprintf(stderr,\"\\n%s: cannot close()\\n\", procname);\n    perror(\"close\");\n    fflush(stderr);\n  }\n}<\/code><\/pre>\n\n\n\n<p>Sitten itse palvelin rutiinit. Http palvelinohjelma:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void http_server()\n{\n  int addr_size;\n  int listenfd;\n\n  struct sockaddr_in sa_cli;\n  struct addrinfo *res;\n\n  unsigned char buffer10&#91;10];\n\n  \/\/ plus space for '\\0'                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n  htmlin = malloc(htmlinlen+1);\n\n  signal(SIGPIPE, SIG_IGN);\n\n  server_getaddrinfo(myport,&amp;res);\n  s = server_socket(res);\n  server_bind(s,res);\n  freeaddrinfo(res);\n  listenfd=server_listen(s);\n\n  dba_main();\n\n  for(;;) {\n    addr_size = sizeof(sa_cli);\n    news = accept(s, (struct sockaddr *)&amp;sa_cli, &amp;addr_size);\n\n    strcpy(htmlip, inet_ntoa(sa_cli.sin_addr));\n    sprintf(htmlport,\"%d\", sa_cli.sin_port);\n\n    unsigned long seconds = dbs_getseconds();\n    strftime(htmltime, sizeof(htmltime), TIMEFORMAT,\n             localtime((time_t *)&amp;seconds));\n\n    fprintf(stdout,\"\\nConnection(http) from %x\", sa_cli.sin_addr.s_addr);\n    fprintf(stdout,\", ip %s\", htmlip);\n    fprintf(stdout,\", port %s\", htmlport);\n    fprintf(stdout,\", at %s\\n\", htmltime);\n\n    int clen = 0;\n    int reads = 0;\n    int first = 1;\n    int bytes, totalbytes=0;\n\n    htmlparams = NULL;\n    htmlin&#91;0]='\\0';\n\n    while(htmlparams == NULL ||\n          clen-strlen(htmlparams) > 0) {\n      if(!first) {\n        fprintf(stdout,\", \");\n      }\n      fprintf(stdout,\"read %d\", reads);\n      fflush(stdout);\n      if((bytes=read(news, htmlin+totalbytes, htmlinlen-totalbytes))&lt;0) {\n        fprintf(stderr,\"\\n%s: cannot read()\\n\", procname);\n        perror(\"read\");\n        fflush(stderr);\n      }\n      fprintf(stdout,\"(%d bytes)\", bytes);\n      if(bytes==0)\n        break;\n      if(bytes>3 &amp;&amp;\n         !isprint(htmlin&#91;0]) &amp;&amp;\n         !isprint(htmlin&#91;1]) &amp;&amp;\n         !isprint(htmlin&#91;2])) {\n        fprintf(stdout,\"https packet?\\n\");\n        htmlin&#91;0]='\\0';\n        totalbytes=0;\n        bytes=0;\n        break;\n      }\n      *(htmlin+totalbytes+bytes) = '\\0';\n      fprintf(stdout,\"%s\\n\",htmlin);\n      totalbytes+=bytes;\n      if(totalbytes>=htmlinlen) {\n        \/\/ plus space for '\\0'                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    \n        htmlin=realloc(htmlin, htmlinlen*2+1);\n        htmlinlen*=2;\n      }\n\n      htmlparams = dbs_html_get_params();\n      dbs_html_get_request_line_num(\"Content-Length\", sizeof(buffer10), buffer10);\n      clen = atoi(buffer10);\n      reads++;\n      first = 0;\n    }\n    if(totalbytes==0)\n      continue;\n    fprintf(stdout,\"\\n%d reads\", reads);\n    fprintf(stdout,\", received %d chars\", totalbytes);\n    fprintf(stdout,\", read %d total bytes\", totalbytes);\n    fprintf(stdout,\", input buffer size %d chars\", htmlinlen);\n    fprintf(stdout,\", data=\\\"%s\\\"\\n\", htmlin);\n\n    dba_loop();\n\n    for(int c=0;c&lt;HTML_BUFFERS;c++) {\n      if((bytes=write(news, dbs_html&#91;c], strlen(dbs_html&#91;c])))==-1) {\n        fprintf(stderr,\"\\n%s: cannot write() dbs_html&#91;%d]\\n\", procname, c);\n        perror(\"write\");\n        fflush(stderr);\n      }\n    }\n\n    server_close(news);\n    fprintf(stdout,\"\\ncallid: %d\\n\", callid++);\n    fflush(stdout);\n  }\n}\n<\/code><\/pre>\n\n\n\n<p>Https palvelinohjelma<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void https_server()\n{\n  int listenfd, status, addr_size;\n\n  SSL_METHOD *method=NULL;\n  SSL_CTX *ctx=NULL;\n  SSL *ssl;\n  X509 *peer_cert;\n\n  struct sockaddr_in sa_cli;\n  struct addrinfo *res;\n\n  FILE *fp1;\n  unsigned char buffer10&#91;10];\n\n  \/\/ plus space for '\\0'                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \n  htmlin = malloc(htmlinlen+1);\n\n  signal(SIGPIPE, SIG_IGN);\n\n  SSL_library_init();\n\n  OpenSSL_add_ssl_algorithms();\n\n  SSL_load_error_strings();\n\n  if((method = (SSL_METHOD *)\n      SSLv23_server_method()) == NULL) {\n    fprintf(stderr,\"\\n%s: cannot SSLv3_server_method()\", procname);\n    fflush(stderr);\n  }\n\n  if((ctx=SSL_CTX_new(method)) == NULL) {\n    fprintf(stderr,\"\\n%s: cannot SSL_CTX_new()\", procname);\n    fflush(stderr);\n  }\n\n  if(SSL_CTX_use_certificate_file(ctx,\n    cert_file, SSL_FILETYPE_PEM) &lt;= 0) {\n    fprintf(stderr,\"\\n%s: cannot SSL_CTX_use_certificate()\", procname);\n    fflush(stderr);\n  }\n\n  if(SSL_CTX_use_PrivateKey_file(ctx,\n      privatekey_file, SSL_FILETYPE_PEM)&lt;=0) {\n    fprintf(stderr,\"\\n%s: cannot SSL_CTX_use_certificate()\", procname);\n    fflush(stderr);\n  }\n\n  if(!SSL_CTX_load_verify_locations(ctx,\n      cert_file, NULL)) {\n    fprintf(stderr,\"\\n%s: cannot SSL_CTX_verify_locations()\", procname);\n    fflush(stderr);\n  }\n\n  server_getaddrinfo(myport, &amp;res);\n  s=server_socket(res);\n  server_bind(s, res);\n  freeaddrinfo(res);\n  listenfd = server_listen(s);\n\n  dba_main();\n\n  for(;;) {\n    addr_size = sizeof(sa_cli);\n    news=accept(s, (struct sockaddr *)&amp;sa_cli, &amp;addr_size);\n\n    strcpy(htmlip, inet_ntoa(sa_cli.sin_addr));\n    sprintf(htmlport,\"%d\", sa_cli.sin_port);\n\n    unsigned long seconds = dbs_getseconds();\n\n    strftime(htmltime, sizeof(htmltime), TIMEFORMAT,\n             localtime((time_t *)&amp;seconds));\n\n    fprintf(stdout,\"\\nConnection(https) from %x\", sa_cli.sin_addr.s_addr);\n    fprintf(stdout,\", ip %s\", htmlip);\n    fprintf(stdout,\", port %s\", htmlport);\n    fprintf(stdout,\", at %s\\n\", htmltime);\n\n    if((ssl=SSL_new(ctx)) == NULL) {\n      fprintf(stderr,\"\\n%s: cannot SSL_new()\", procname);\n      fflush(stderr);\n    }\n\n    if(SSL_set_fd(ssl,news) != 1) {\n      fprintf(stderr,\"\\n%s: cannot SSL_set_fd()\", procname);\n      fflush(stderr);\n    }\n\n    if((status=SSL_accept(ssl))&lt;0) {\n      \/\/ Try to fix SSL_accept error 5\n      if(SSL_get_error(ssl,status) == 5) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        \n        fprintf(stdout,\", access failed, error 5, retry\");\n        SSL_free(ssl);\n        continue;\n      }\n      \/\/ Try to fix SSL_accept error 1\n      if(SSL_get_error(ssl,status) == 1) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        \n        fprintf(stdout,\", access failed, error 1, retry\");\n        SSL_free(ssl);\n        continue;\n      }\n      fprintf(stderr,\"\\n%s: cannot SSL_accept(), status: %d, SSL error: %d\",\n          procname, status, SSL_get_error(ssl,status));\n      fflush(stderr);\n    }\n\n    peer_cert = SSL_get_peer_certificate(ssl);\n    if(peer_cert == NULL) {\n      fprintf(stdout,\", No peer certificate\");\n      fflush(stdout);\n    }\n    fprintf(stdout,\"\\n\");\n\n    int clen = 0;\n    int reads = 0;\n    int first = 1;\n    htmlparams = NULL;\n    int bytes, totalbytes = 0;\n    while(htmlparams==NULL || clen-strlen(htmlparams) > 0) {\n      if(!first) {\n        fprintf(stdout,\", \");\n      }\n      fprintf(stdout,\"read %d\", reads);\n      fflush(stdout);\n      if((bytes = SSL_read(ssl, htmlin+totalbytes, htmlinlen-totalbytes))&lt;0) {\n        fprintf(stderr,\"\\n%s: cannot SSL_read()\\n\", procname);\n        perror(\"SSL_read\");\n        fflush(stderr);\n      }\n      fprintf(stdout,\"(%d bytes)\", bytes);\n      if(bytes==0)\n        break;\n      *(htmlin+totalbytes+bytes) = '\\0';\n\n      totalbytes+=bytes;\n      if(totalbytes >= htmlinlen) {\n        \/\/ plus space for '\\0'                                                                                                                                                                                                                                                                                                   \n        htmlin=realloc(htmlin, htmlinlen*2+1);\n        htmlinlen *= 2;\n      }\n\n      htmlparams = dbs_html_get_params();\n      dbs_html_get_request_line_num(\"Content-Length\",\n          sizeof(buffer10), buffer10);\n      clen = atoi(buffer10);\n      reads++;\n      first = 0;\n    }\n\n    fprintf(stdout,\"\\n%d reads\", reads);\n    fprintf(stdout,\", received %d chars\", totalbytes);\n    fprintf(stdout,\", read %d total bytes\", totalbytes);\n    fprintf(stdout,\", input buffer size %d chars\", htmlinlen);\n    fprintf(stdout,\", data=\\\"%s\\\"\\n\", htmlin);\n\n    dba_loop();\n\n    for(int c=0;c&lt;HTML_BUFFERS;c++) {\n      if((status=SSL_write(ssl, dbs_html&#91;c], strlen(dbs_html&#91;c])))&lt;1) {\n        fprintf(stderr,\"\\n%s: cannot SSL_write(), buffer %d, status: %d, SSL error: %d\",\n                procname, c, status, SSL_get_error(ssl,status));\n        fflush(stderr);\n      }\n    }\n    fprintf(stdout,\"\\nSSL connection using %s\", SSL_get_cipher(ssl));\n    fflush(stderr);\n    SSL_free(ssl);\n\n    server_close(news);\n\n    fprintf(stdout,\"\\ncallid: %d\\n\", callid++);\n    if((fp1 = fopen(\"dbscallid.deb\",\"w\")) != NULL) {\n      fprintf(fp1,\"%d\\n\", callid);\n      fclose(fp1);\n    }\n\n    fflush(stdout);\n  } \/\/ for(;;)                                                                                                                                                                                                                                                                                                                   \n  SSL_CTX_free(ctx);\n}\n\n<\/code><\/pre>\n\n\n\n<p>P\u00e4\u00e4ohjelma<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>int main(int argc,char *argv&#91;])\n{\n  int c,https = 1;\n  unsigned char filedigest&#91;HashLen],buffer10&#91;10];\n\n  procname=argv&#91;0];\n\n  dbs_file_digest(\"\/proc\/self\/exe\", filedigest);\n  htmldigest&#91;0]='\\0';\n  for(int c = 0;c &lt; HashLen; c++) {\n    char twodigits&#91;3];\n    sprintf(twodigits,\"%02x\", filedigest&#91;c]);\n    strcat(htmldigest, twodigits);\n  }\n\n  callid = 0;\n\n  FILE *fp1;\n\n  if((fp1 = fopen(\"dbscallid.deb\",\"r\")) != NULL) {\n    fgets(buffer10, sizeof(buffer10), fp1);\n    callid = atoi(buffer10);\n    fclose(fp1);\n  }\n\n  if((fp1 = fopen(\"dbspid.deb\",\"w\")) != NULL) {\n    fprintf(fp1,\"%d\\n\", getpid());\n    fclose(fp1);\n  }\n\n  myport = DEFAULT_PORT;\n\n  for(c = 1; c &lt; argc; c++) {\n    if(!strncmp(argv&#91;c], \"--https\",7))\n      https = 1;\n    else if(!strncmp(argv&#91;c], \"--http\",6))\n      https = 0;\n    else if(!strncmp(argv&#91;c], \"--port\",6)) {\n      if(argv&#91;c]&#91;6] != '\\0')\n        myport = save_string(&amp;argv&#91;c]&#91;6]);\n      else\n        myport = save_string(argv&#91;++c]);\n    } else if(!strncmp(argv&#91;c], \"-p\",2)) {\n      if(argv&#91;c]&#91;2] != '\\0')\n        myport = save_string(&amp;argv&#91;c]&#91;2]);\n      else\n        myport = save_string(argv&#91;++c]);\n    } else {\n      fprintf(stderr,\"%s: Invalid option %s\\n\",\n          procname, argv&#91;c]);\n      fflush(stderr);\n      exit(1);\n    }\n\n  }\n  fprintf(stdout,\"%s\", programname);\n  if(https)\n    fprintf(stdout,\", https\");\n  else\n    fprintf(stdout,\", http\");\n\n  fprintf(stdout,\", port %s\", myport);\n\n  fprintf(stdout,\", sha256(%s)\\n\", htmldigest);\n  fprintf(stdout,\"%s\", copyright);\n  fflush(stdout);\n\n  dba_main(argc,argv);\n\n#ifdef RESSU\n  ressu_init();\n#endif\n#ifdef FORT\n  fort_init();\n#endif\n\n  if(https)\n    https_server();\n  else\n    http_server();\n\n  exit(0);\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kaikki oikeudet pid\u00e4tet\u00e4\u00e4n. Olen jo jonkin aikaa ihmetellyt satunnaisbittej\u00e4 ja terttu on j\u00e4\u00e4nyt v\u00e4hemm\u00e4lle huomiolle. Jos haluat lukea valmiimmista ohjelmista, t\u00e4ss\u00e4 fort (https:\/\/moijari.com\/?p=964), t\u00e4ss\u00e4 ressu1.7 (https:\/\/moijari.com\/?p=798), ja t\u00e4ss\u00e4 ressu1.8 (https:\/\/moijari.com\/?p=842). T\u00e4ss\u00e4 postissa esitell\u00e4\u00e4n uusi yritys tertusta. Ohjelma koostuu tietokannasta (DB2) ja html-lomakeohjelmasta (DBS). db_ alkuiset rutiinit kuuluvat tietokantaan ja dbs_alkuiset rutiinit html-liittym\u00e4\u00e4n. Postin aluksi k\u00e4yd\u00e4\u00e4n&hellip; <a class=\"more-link\" href=\"https:\/\/moijari.com\/?p=1256\">Continue reading <span class=\"screen-reader-text\">Jatkoa tertulle DB2, DBS ja DBA 0.05<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/posts\/1256"}],"collection":[{"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/moijari.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1256"}],"version-history":[{"count":174,"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/posts\/1256\/revisions"}],"predecessor-version":[{"id":1460,"href":"https:\/\/moijari.com\/index.php?rest_route=\/wp\/v2\/posts\/1256\/revisions\/1460"}],"wp:attachment":[{"href":"https:\/\/moijari.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1256"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/moijari.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1256"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/moijari.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1256"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}