Category: Uncategorized

  • [Ratkaisu] Tekstintunnistuksella tuotetun tekstitystiedoston virheiden korjaaminen

    Lähtökohta
    Kotihakemistossani on bin-alihakemisto ja se on määritelty komentojenhakupolkujen (PATH-muuttuja) joukkoon komentoriviprofiilissani.

    Olen muuntanut kuvamuotoisen tekstityksen tekstimuotoiseksi Avidemuxilla. SRT-muotoinen tekstitystiedosto on tallennettu kotihakemistooni tiedostonimellä tekstitys.srt. Tekstitys sisältää muunnoksen jäljiltä joitain toistuvia virheitä: rivien alussa on välilyöntejä, pikku-l ja iso I-kirjain ovat paikoin sekoittuneet, joidenkin numeroiden välissä on välilyöntejä vaikka numerot ovat osa samaa lukua, ja lainausmerkin (“) tilalla on kaksi heittopilkkua (‘ ‘).

    Ongelma
    Virheitä on niin paljon, että niiden korjaaminen käsityönä Tekstieditorissa olisi liian työlästä. Haluan korjata virheet tarkoitukseen soveltuvilla komentojonoilla.
    Ratkaisu
    Luon seuraavassa listatut komentojonotiedostot, ja tallennan ne kotihakemistossani olevaan bin-alihakemistoon.

    #!/bin/sed -f
    s/^\ *//

    Lataa leading_blank.sed (24 tavun komentojonotiedosto)

    #!/usr/bin/gawk -f
    
    BEGIN { FS = "" }
    {
      previous = ""
      for (i = 1; i <= NF; i = i + 1) {
        if ($i ~ /I/) {
          if (previous ~ /[[:lower:]]/) {
            printf("l")
            previous = "l"
          } else if (i == 1) {
            printf("%s",$i)
            previous = $i
          } else if ((previous ~ /-/) && (i == 2)) {
            printf("%s",$i)
            previous = $i
          } else if (i < NF) {
            for (j = 1; i + j < NF; j = j + 1) {
              if ($(i+j) ~ /[[ \n]]/) {
                j = NF
                break
              } else if ($(i+j) ~ /[[:lower:]]/) {
                break
              }
            }
            if (i + j < NF) {
              printf("l")
              previous = "l"
            } else {
              printf("%s",$i)
              previous = $i
            }
          } else {
            printf("%s",$i)
            previous = $i
          }
        } else {
          printf("%s",$i)
          previous = $i
        }
      }
      printf("\n")
    }

    Lataa mixed_verticals.gawk (756 tavun komentojonotiedosto)

    #!/bin/sed -f
    s/\([[:digit:]]\) \([[:digit:]]\)/\1\2/g

    Lataa blank_between_numbers.sed (56 tavun komentojonotiedosto)

    #!/bin/sed -f
    s/''/"/g

    Lataa double_single_quotes.sed (24 tavun komentojonotiedosto)

    Tämän jälkeen teen komentojonotiedostoista suoritettavia seuraavalla komennolla:

    cd ~/bin && chmod +x blank_between_numbers.sed leading_blank.sed \
    mixed_verticals.gawk double_single_quotes.sed

    Lopuksi käytän kaikkia komentojonoja tekstitystiedostoon sen korjaamiseksi, seuraavasti:

    cat ~/tekstitys.srt \
    | leading_blank.sed | mixed_verticals.gawk \
    | blank_between_numbers.sed | double_single_quotes.sed \
    > ~/korjattu-tekstitys.txt

    Tämän jälkeen kotihakemistossani oleva korjattu-tekstitys.txt -niminen tiedosto sisältää tekstityksen, jossa alkuperäisessä olleet virheet on korjattu.

  • Googlen favicon

    Juuri kun aloin tottua Googlen uuteen ikoniin, törmäsin Greasemonkey-skriptiin, joka palauttaa sen vanhan faviconin. Vielä viikko sitten olisin ollut innoissani ja ottanut tuon heti käyttöön, mutta nyt hämääntyisin siitä vanhasta luultavasti vain uudestaan samalla lailla kuin tuosta uudesta sen ilmestyttyä.

  • [Ratkaisu] Ala etsiä tekstistä heti kirjoitettaessa ja Edellinen- ja Seuraava-napit

    Lähtökohta
    Olen valinnut Firefox-selaimen asetuksissa Ala etsiä tekstistä heti kirjoitettaessa -toiminnon käyttöönotetuksi. Firefoxin kolmosversiossa tämän pikahakutoiminnon avaama työkalupalkki on typistetty pelkästään hakusanakentäksi. Koska haluan, että pikahakutoiminnon työkalupalkki sisältää samat valitsimet kuin normaali hakutoiminto (jonka voi avata esimerkiksi Control + F -pikanäppäimellä), olen lisännyt selaimen profiilihakemistossani olevaan userChrome.css -tiedostoon seuraavanlaisen rivin:

    #FindToolbar > * {display: -moz-box; }

    Tämän jälkeen heti kirjoitettaessa avautuvan hakutoiminnon työkalupalkki sisältää normaalin hakutyökalupalkin tavoin muun muassa valitsimet Edellinen, Seuraava ja Huomioi kirjainkoko.
    Olen päivittänyt Firefox-selaimen versiosta 3 Beta 5 versioon 3 RC 1.

    Ongelma
    Päivityksen jälkeen heti kirjoitettaessa avautuvan hakutoiminnon työkalupalkki on jälleen typistynyt pelkäksi hakusanakentäksi.
    Ongelman syy
    Pikahakutyökalupalkin valitsin on vaihtunut päivityksen myötä. Uusi valitsin on .findbar-container.
    Ratkaisu
    Korvaan userChrome.css -tiedostossa olevan, yllä mainitun rivin seuraavanlaisella rivillä:

    .findbar-container > * { display: -moz-box; }

    Kun tämän jälkeen suljen Firefox-selaimen, ja sen jälkeen käynnistän sen uudestaan, heti kirjoitettaessa avautuva etsintätyökalupalkki sisältää samat valitsimet kuin normaali hakutoimintokin.

  • Komentohistoriatilastomeemi

    Via Miia.

    jani@shuttle-xpc:~$ history|awk '{a[$2]++ } END{for(i in a){print a[i] " " i}}'|sort -rn|head
    72 sudo
    58 ls
    39 man
    34 less
    29 xrandr
    26 cd
    22 ifconfig
    20 dmesg
    19 cp
    16 grep
  • Ikkunasta otettu kuvakaappaus ei sisällä ikkunan kehyksiä

    Lähtökohta
    Olen asettanut /apps/gnome-screenshot/include_border -avaimen arvoksi true. Olen avannut Tomboy-muistilapun niin, että muistilappu näkyy omassa pienessä ikkunassaan. Haluan ottaa ikkunasta kuvakaappauksen, joten painan näppäinyhdistelmää Alt + Print Screen. Tallenna kuvakaappaus -ikkuna avautuu, ja sen esikatselunäkymässä on muistilappuni sisältö.
    Ongelma
    Muistilappikkunan kehykset eivät ole mukana kuvakaappauksessa. Haluan, että ikkunan raamit otetaan mukaan kuvakaappaukseen, kuten include_border -avaimen arvo määrää.
    Ongelman syy
    Se, etteivät ikkunan kehykset tule kuvaan mukaan, on ohjelmavirhe tulkinnasta riippuen joko GNOME-työpöytäympäristön kuvankaappaussovelluksessa tai sitten X-ikkunointiympäristössä.
    Ratkaisu
    Ratkaisua ei vielä ole. Ongelman voi kiertää kahdella eri tavalla:

    • Käytän kuvakaappauksen ottamiseen pelkkää Print Screen -näppäintä Alt + Print Screen -yhdistelmän sijasta. Tällöin kaapattu kuva sisältää koko työpöytänäkymän, josta voin sen jälkeen rajata haluamani ikkunan kehyksineen esimerkiksi gThumb-kuvankatselimella.
    • Käytän kuvakaappauksen ottamiseen GIMP-kuvankäsittelyä:
      1. Käynnistän GIMP-kuvankäsittelyn valitsemalla sen Sovellukset → Grafiikka -valikosta.
      2. GIMPin käynnistyttyä valitsen sen pääikkunan Tiedosto-valikossa olevasta Hanki-alivalikosta kohdan Kuvankaappaus....
      3. Valitsen avautuvasta Kuvankaappaus-ikkunasta kohdan Ota kuvankaappaus yhdestä ikkunasta ja varmistan, että siihen liittyvä kohta Sisällytä ikkunan reunat on käyttöön valittuna.
      4. Edelleen Kuvankaappausikkunassa asetan Viiveen arvoksi 5 sekuntia.
      5. Valitsen Snap-napin, jonka jälkeen otan Tomboy-muistilappuikkunan esille ennen kuin viiden sekunnin viive on kulunut loppuun. Jätän ikkunan valituksi ja odotan viiveen loppuunkulumista. Kun viive on kulunut loppuun, hiiren osoitin muuttuu ristin muotoiseksi.
      6. Osoitan muistilappuikkunan yläraamia ja painan kerran hiiren vasenta nappia.

      Tämän jälkeen kuvakaappaus muistilappuikkunasta ilmestyy käsiteltäväkseni GIMP-kuvankäsittelyn ikkunassa, josta voin tallentaa sen.

  • WordPress URL parameter for getting the comments feed

    The parameter you need to apply to WordPress’ index.php in order to get the feed for comments seems bizarrely eluding, so I’ll make a note of it here for future reference: it’s feed=comments-rss2. So when a WP blog at http://domain.com/blog/ doesn’t have pretty permalinks (which renders the comments feed URL to http://domain.com/blog/comments/rss2), you’ll find the comments feed at http://domain.com/blog/?feed=comments-rss2.

  • [Ratkaisu] Inklusiivinen käyttöoikeustason kohotus PAM USB:n avulla

    Lähtökohta
    Pakettienhallinnassa pamusb-tools ja libpam-usb ovat asennettuna. Olen määritellyt PAM USB:n avulla tunnistautumismenetelmäkseni USB-porttiin kytketyn muistitikun.
    Ongelma
    Turvallisuuden vuoksi haluan sulkea USB-muistitikulla tunnistautumisen oletusarvoisesti pois käytöstä, ja määritellä sen erikseen käyttöönotetuksi niiden palveluiden osalta, joissa haluan sitä hyödyntää: järjestelmän asetusten tekemisen ja ylläpitotöiden vaatiman oikeustason kohotuksen yhteydessä.
    Ratkaisu
    Määrittelen /etc/pamusb.conf -tiedostoni alussa seuraavanlaisen osion:

    <defaults>
      <option name="enable">false</option>
    </defaults>

    Kun olen tallentanut tiedostoon tekemäni muutokset, muistitikulla tunnistautuminen on oletusarvoisesti pois käytöstä.
    Määrittelen sen jälkeen hieman alempana seuraavanlaisen osion:

    <services>
      <service id="sudo">
        <option name="enable">true</option>
      </service>
      <service id="gksudo">
        <option name="enable">true</option>
      </service>
      <service id="polkit">
        <option name="enable">true</option>
      </service>
    </services>

    Kun olen tallentanut tiedostoon tekemäni muutokset, USB-portissa oleva muistitikku riittää tunnistautumiseksi järjestelmän asetusten tekemisen ja ylläpitotöiden vaatiman oikeustason kohotuksen yhteydessä. Sen sijaan esimerkiksi sisäänkirjautuminen ja näytön lukituksen avaaminen vaativat edelleen salasanan kirjoittamisen.

    Huomautuksia
    • Tämä ratkaisu on sinänsä varsin triviaali sovellus PAM USB:n asetusten tekemisen ohjeista, mutta muiden muassa Verkko-ylläpitosovelluksen käyttäjän tunnistamisessa käyttämään PolicyKitiin liittyvää palvelun nimeä ei löytynyt suoraan annettuna mistään, joten päätin kirjata sen ylös tähän, vaikka sekin on sinänsä varsin helppo päätellä.
    • Palveluiden nimien löytämisessä on avuksi /var/log/auth.log -tiedosto. Kun muistitikulla tunnistautumisen sallii tilapäisesti oletusarvoisesti kaikkialla, tiedostoon kirjautuu halutun palvelun käytön yhteydessä seuraavanlainen rivi:
      Jun  3 10:30:20 shuttle-xpc pam_usb[17130]: Authentication request for user "jani" (polkit)

      Rivillä käyttäjätunnuksen perässä, suluissa oleva merkkijono on palvelun nimi.

    • Yllä mainittujen osioiden lisäksi olen määritellyt asetustiedostossa USB-muistitikun tunnistautumismenetelmäksi ainoastaan omalla käyttäjätunnuksellani, seuraavanlaisella osiolla:
      <users>
        <user id="jani">
          <device>
            MicroMemory
          </device>
        </user>
      </users>

      Tämä vaatii luonnollisesti asetustiedostoon lisäksi MicroMemory-nimisen määrittelyn itse muistitikulle.

  • WordPress comment anonymiser plugin

    I’ve thrown together a little plugin which sorta anonymises the comments your WordPress blog receives. As you probably know, WordPress stores your commenters’ IP addresses to help you fight spam and other forms of abuse. Obviously, this also helps authorities in tracking down people for saying things that are forbidden in your society. If you believe in freedom of speech as vehemently as I do, you wouldn’t want them to be able to do that, and you certainly wouldn’t want to provide them with such unintended assistance in their hunt for people exercising freedom of expression.

    This is where IP Address Eraser may help. It replaces the commenter’s IP address with a static address (by default, 127.0.0.1), so in order to track down the commenter, you would have to have access to your ISP’s web server logs. Note that through this route, your commenters might and probably will still be exposed, depending on your local laws regarding data retention by ISPs. That’s why I wouldn’t recommend that you advertise the fact that you’re using this plugin to your commenters, as they might be left with a false sense of security. If you want them to be truly safe (or at least, as safe as they can be), I suggest you recommend that they use Tor.

    Also note that, again, depending on your local legislation, you might yourself be held accountable for your commenters’ opinions. Unfortunately, this is equally true even without the use of this plugin.

    A technical note: Akismet, the default spam-fighting plugin employed by WordPress, takes a precedence in the order of filters employed to comments. I’ve not verified this, but I suspect that makes it use the comment poster’s IP address prior to it being erased by IP Address Eraser. I haven’t come across any tangible compatibility problems, so I’m hoping there are none, but please don’t take my word for it. I’m publishing this in the hopes that it’ll be useful, but there’s absolutely no warranty of any kind associated with this software.

    Another, final techical note: What I’d really hope to have is the best of both worlds. It would be a plugin which wouldn’t erase the IP addresses right away, but after a set delay (say, 24 hours). This would put you in charge: to be able to use the data to tackle abuse when you deem necessary, yet leave you unworried about storing privacy-compromising data for far too long (forever, which is the WordPress default). Unfortunately my WordPress hacking skills are too lame to come up with such a plugin. If anybody takes a crack at it, I’d be interested to know. Please, feel free to use/abuse my plugin in doing so.

  • Banging my head against C pointer dereference

    The pointers always get me when I try my hands at C. Now, I’m not dumber than most people, and the basic idea is pretty clear to me. But what trips my brain each time is the notation of declaration.

    It’s not always illogical. After all, as K&C points out, int *ip is intended as a mnemonic; it says that the expression *ip is an int. Okay, that seems innocent enough, but in fact it’s a two-edged sword. What about when you’re declaring a pointer as a parameter for a function, then calling the function? Compare these two short programs below:

    Listing 1. Listing 2.
    int foo(int i);
    int main() {
     int j = 1;
     foo(j);
     return 0;
    }
    
    int foo(int i) {
      /* do something with i */
      return 0;
    }
    int foo(int *ip);
    int main() {
      int j = 2;
      foo(&j);
      return 0;
    }
    
    int foo(int *ip) {
      /* do something with (*)ip */
      return 0;
    }

    For the first program, it’s easy to see that the parameter of foo(), i, is assigned the contents of j from main(): you’re in effect saying:

    1. Let foo() have one parameter, i, of type int.
    2. From main(), assign the value of j, also of type int, to i.

    But for the second one, foo() has to be called with an address (a pointer), and yet the declaration, with the mnemonic notation in place, seems to call for an int:

    1. Let foo() have one parameter, *ip, of type int.
    2. From main(), assign the value of &j, of type… uhh… pointer, to… uhh… ???

    This difficulty for me to grasp what type of a parameter foo() should be called with is due to the mnemonic notation in int *ip: when you have an int <something> as a parameter declaration, you expect to assign the parameter something that is of type int — not something that is a pointer to an int.

    Now, if I give up the mnemonic and instead write the declaration as int* ip, the code translates to natural language quite fluidly:

    Listing 3.
    int foo(int* ip);
    int main() {
      int j = 2;
      foo(&j);
      return 0;
    }
    
    int foo(int* ip) {
      /* do something with (*)ip */
      return 0;
    }
    1. Let foo() have one parameter, ip, which is a pointer to an int.
    2. From main(), assign the value of &j, also a pointer to an int, to ip.

    Of course, this notation opens a whole new can of worms. Consider the following declaration:

    int* i, j;

    Is j a pointer now, or just an int? It’s way too open to misinterpretation to make this notation commendable despite the previous advantage in readability.

    So I’m better off sticking to the mnemonic notation, and just trying to get my head around it. For that, I’m still in desperate need for an easy translation of such code into natural language.

    Maybe the mnemonic is the root of the problem. It’s a really bad mnemonic because it almost always works, but then there’s (at least) this one case, where you’re better off not remembering it, because it screws up your logic if you do. So I should think of int *ip as ip is a pointer to an int and just forget about the fact that it resembles a declaration of an int called *ip.