List ov dīari entriz in ol langwijz.
So I'm writing an IMAP client for Emacs. The main reason is that I want an email client works as if by osmosis, like the one on my phone—whenever it has a connection, messages magically enter a local cache, where they stay available even if the connection is lost. There are many ways to achieve that with existing Emacs email clients, possibly augmented with external tools, but I want one that works with minimal setup, and requires nothing but Emacs itself.
The first problem encountered when writing an IMAP client is parsing
what the server is sending. There are two IMAP parsers in the Emacs
source tree already (imap.el
and nnimap.el
), both fairly closely
tied to their respective clients, and both synchronous—they block
Emacs while they are waiting for data from the server. I want to
address both of those points: my IMAP client is going to have a
reusable asynchronous parser module.
Obviously, the first step in figuring out how to parse the protocol is to read the IMAP RFC. Naturally, you'd skip ahead to the section "Formal syntax", which defines in ABNF what various messages should look like: a sequence number followed by one of a few keywords followed by a quoted string or an atom, etc etc. That's where I started, adding a special case for each command response into my parser code.
But then I thought that this wasn't the right way to do it. The parser module wouldn't be independent of the module that uses it, since the calling module might want to use an extension that the parser module doesn't know about yet. Also, I would constantly need to come up with sensible data structures to represent the responses with.
So I decided to create a parser that would be able to parse IMAP responses with as little knowledge as possible about what those responses should look like. It turns out that most of IMAP consists of atoms, strings and lists. I figured that I should be able to parse everything following these simple rules:
- If it starts with a double quote, parse it as a quoted string.
- If it starts with a
(
or[
, descend, parse recursively and return it as a list. - Otherwise, treat it as an atom, read until the next space character
or closing
)
or]
, and return it as a string.
For example, this LIST
response:
* LIST (\HasNoChildren) "." INBOX
gets parsed to this:
("*" "LIST" ("\\HasNoChildren") "." "INBOX")
This mostly worked. One exception is that if the second word of a
line is one of OK
, NO
, BAD
, BYE
or PREAUTH
, then the rest of
the line (described by resp-text
in the RFC) is free-form
human-readable text, optionally preceded by a resp-text-code
. For
example, this is part of a response to a SELECT
command from the
Dovecot server:
* OK [UNSEEN 6] First unseen.
* OK [UIDVALIDITY 1381959933] UIDs valid
* OK [UIDNEXT 10] Predicted next UID
1 OK [READ-WRITE] Select completed (0.003 secs).
There is no reason why this free-form text couldn't contain unbalanced parentheses or anything else that might confuse a parser, and besides it doesn't make sense to split the text into words anyway. So that's a special case for the parser, and gets parsed to this:
("*" :ok :code "UNSEEN" :data "6" :text "First unseen.")
("*" :ok :code "UIDVALIDITY" :data "1381959933" :text "UIDs valid")
("*" :ok :code "UIDNEXT" :data "10" :text "Predicted next UID")
("1" :ok :code "READ-WRITE" :data nil :text "Select completed (0.003 secs).")
Then there is BODY
, which when given as a fetch or message attribute
is followed immediately by an opening square bracket, e.g. BODY[]
.
I decided to treat it as if there were a space between BODY
and the
bracket, and return them as two elements, the string "BODY"
and the
list of items parsed inside the brackets.
And all of this applies in principle to every single line, except that
a physical line can end with a byte count in curly braces
(e.g. {42}
), which means that the following bytes are literal data
to be treated as part of the current logical line. Fortunately, this
is independent from the parsing itself, so I have a function that
splits the data by logical lines and passes everything to the parse
function.
So far, my little client is able to select a mailbox, search for unread messages and fetch the messages, and my parser is sufficient for all of that.
Emacs 24 introduced the possibility to open TLS network connections using the GnuTLS library directly, instead of using a command line tool as a wrapper. This is especially interesting for those who are stuck using Emacs on Windows, as the command line tools can be rather brittle on that platform.
However, there are some steps that need to be performed in order to get native GnuTLS to work. This page attempts to describe them.
Get a GnuTLS-enabled Emacs
The Windows binaries available for download from the GNU site are
compiled against GnuTLS, but if you compile your own Emacs, see the
file nt/INSTALL
in the Emacs source distribution for instructions.
Find the GnuTLS DLLs
The first google hit for "emacs gnutls windows" is this page. It says:
There's one way to find out if GnuTLS is available, by calling
gnutls-available-p
. This is a little bit trickier on the W32 (Windows) platform, but if you have the GnuTLS DLLs (available from http://sourceforge.net/projects/ezwinports/files/ thanks to Eli Zaretskii) in the same directory as Emacs, you should be OK.
On that page, I found:
gnutls-3.0.9-w32-bin.zip 2012-01-02 7.2 MB
Extract the GnuTLS DLLs
I first naïvely tried opening the zip file in Explorer and copying the
files from there, but that does nothing—it neither copies the files
nor displays any error message. You need to extract the zip file, and
then copy all DLL files to the bin
directory where your Emacs is
installed, probably somewhere like
C:\Program Files (x86)\emacs-24.3\bin
.
Restart Emacs and try it
At this point, if you restart Emacs and type:
M-: (gnutls-available-p) RET
you should see t
in the echo area, which means that Emacs can find
the GnuTLS libraries.
Configure trust files
However, if you try to open a TLS connection, it will fail complaining
that certificate validation failed. This happens because GnuTLS needs
to have a set of CA certificates to verify the certificates of the
servers it connects to. It looks for CA certificates in the locations
specified in the variable gnutls-trustfiles
, but none of the default
values work out of the box on Windows.
I'm not aware of any way to make GnuTLS use any certificates that come
with the Windows system, so you need to get a certificate bundle from
elsewhere. The cURL project
provides such a bundle that
you can download. Download the cacert.pem
file to a suitable
location, and point gnutls-trustfiles
to it with customize-option
.
Note that the file name is passed unexpanded to GnuTLS, so you cannot
use ~
as a shorthand for your home directory; use the full absolute
file name instead.
See if it works
Paste the following piece of code into the *scratch*
buffer:
(condition-case e
(delete-process
(gnutls-negotiate
:process (open-network-stream "test" nil "www.google.com" 443)
:hostname "www.google.com"
:verify-error t))
(error e))
Then put point at the end and hit C-j
. If nil
gets inserted into
the buffer, then the certificate could be verified, and your setup
appears to be working.
Otherwise, you'll see an error like:
(error "Certificate validation failed www.google.com, verification code 66")
If so, a good place to start debugging is setting the variable
gnutls-log-level
to a value greater than 0.
In Emacs 23 or later, you can use M-x proced
to get a list of
running processes. You can sort and filter them in various ways, the
most common end goal being to quickly find and kill certain processes
in my experience.
However, Proced currently doesn't work on OSX. I just spent a few minutes figuring out why, so I hope writing this could save someone else some effort.
Proced is not based on command line tools such as ps
, but uses
Emacs Lisp functions implemented in C to get the list of processes
(list-system-processes
) and to get process attributes
(process-attributes
). list-system-processes
works on OSX, but
process-attributes
does not.
sysdep.c
contains a few different implementations guarded by #ifdef
s, and
this is the one chosen for OSX:
Lisp_Object
system_process_attributes (Lisp_Object pid)
{
return Qnil;
}
In March 2010, this was discussed on the macosx-emacs mailing list, and a patch giving parts of the needed information yielded a screenshot, but it seems that it wasn't finished, nor merged into Emacs.
Update: I got bored and wrote a patch for it. It basically works, but there are some things that it currently doesn't retrieve, notably memory and CPU usage, and command line arguments.
Emacs' process editor (proced) works on many operating systems, but not OSX. Here is why.
In Erlang, if a prōses iz runing cōd frm modyul a
wen modyul a
iz
rēlōdd, it duznt get outomaticli upgrādd tu th nyu vurjhn. Th ōld vurjhn ov
th cōd iz kept az long az eni prōses iz yuzing it, nd th prōses iz sed tu b
runing "ōld cōd".
Hauever, ōnli won ōld vurjhn iz kept, so if th prōses duznt swich tu th nyu vurjhn bifor th modyul iz rēlōdd fr a secnd tīm, th prōses wil b cild bī th cōd survr. This can b cnfyuzing, az thr iz oftn no trās ov th prōses dīng, nd ēvn if thr iz, yu ōnli get tu nō that th prōses woz cild, but not y.
Wīl chāzing daun a problm rilātd tu this, Ī cām up with this pach tu th cōd survr:
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 00ad923..c4d5fd6 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -1414,6 +1414,7 @@ do_purge(Mod0) ->
do_purge([P|Ps], Mod, Purged) ->
case erlang:check_process_code(P, Mod) of
true ->
+ catch info_msg("Killing ~p for old code from ~p", [P, Mod]),
Ref = erlang:monitor(process, P),
exit(P, kill),
receive
That led mē strāt tu th modyul that woz bēng rēlōdd, nd let mē fix th problm bī enshoring that th prōses swichd tu nyu cōd.
This pach iz probubli not sūtbl fr inclūjhn in th ofishl Erlang/OTP sorsiz, but Ī hōp it can b yusfl wen diveluping.
In Erlang, if a process is running code from module a
when module
a
is reloaded, it doesn't get automatically upgraded to the new
version. The old version of the code is kept as long as any process
is using it, and the process is said to be running "old code".
However, only one old version is kept, so if the process doesn't switch to the new version before the module is reloaded for a second time, the process will be killed by the code server. This can be confusing, as there is often no trace of the process dying, and even if there is, you only get to know that the process was killed, but not why.
While chasing down a problem related to this, I came up with this patch to the code server:
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 00ad923..c4d5fd6 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -1414,6 +1414,7 @@ do_purge(Mod0) ->
do_purge([P|Ps], Mod, Purged) ->
case erlang:check_process_code(P, Mod) of
true ->
+ catch info_msg("Killing ~p for old code from ~p", [P, Mod]),
Ref = erlang:monitor(process, P),
exit(P, kill),
receive
That led me straight to the module that was being reloaded, and let me fix the problem by ensuring that the process switched to new code.
This patch is probably not suitable for inclusion in the official Erlang/OTP sources, but I hope it can be useful when developing.
How can you know when the code server kills your process, and why?
So I was dusting off an old Windows XP machine, applying security
updates for the last few years, when suddenly I found that the machine
would crash with a BSOD a few minutes after boot. The BSOD said that
a DRIVER_IRQL_NOT_LESS_OR_EQUAL
error had occured in w22nt51.sys.
Googling that lead to this article, which revealed that
this is a problem in the driver for the Intel 2200BG wireless network
card.
Broken network drivers usually lead to chicken-and-egg problems, and that article suggests a way around that, but fortunately I had an Ethernet cable at hand and was not entirely dependent on wireless network. (The cable was in my "give away or throw away" bag, so I'm grateful for my laziness and will reconsider some of the items in it.)
So I solved this by the following steps:
- Boot into "Safe mode" (without networking).
- Deactivate the wireless network card in the "System" section of the control panel, to stop the crashes.
- Boot into "Safe mode with networking" (though a normal boot would probably have been sufficient at this point) and connect the Ethernet cable.
- Download the new driver from the Intel Download Center.
- Install the driver.
- Reboot, to make sure the old driver is not in use (not sure if necessary).
- Reactive the device in the control panel.
And it works!
So why am I subjecting myself to Windows XP again? Simply because the firmware upgrade tool for my HTC phone requires Windows. Also, now I have the opportunity to test whether my Emacs-based Jabber client works well with Windows. (One of these days…)
Mi komencis scivoli, kial restas tiel multe da monato je la fino de la mono.
Mi ja havas la konteltirojn de la banko, do mi povus rigardi ilin kaj mem sumigi kion mi pagis al kiu, sed tiun laboron la komputilo devus fari por mi, ĉu ne? Pasintjare mi komencis por tio uzi hledger. Tio kio plej logis min al ĝi estis ke ĝi konservas transakciojn en teksta dosiero. Tia dosiero estas facile redaktebla per ajna tekstoredaktilo, kaj se pro iu kialo mi ne plu povus uzi tiun programon, mi ĉiukaze povus legi miajn datumojn.
Iel mi devas enmeti la datumojn en la programon. Mia banko permesas elŝuti konteltirojn en "CSV-formato" (datumeroj disigitaj per komoj). hledger havas funkcion por legi transakciojn el CSV-dosiero, dividi la transakciojn en kategoriojn (ekz. manĝo, lupago, transporto ktp) surbaze de la priskriba teksto kaj dosiero kun reguloj, kaj krei tekstan dosieron laŭ la formato de hledger. La procedo estis la jena:
- Elŝuti dosierojn de la banka retejo. Estas po unu dosiero por ĉiu konto (kuranta konto, ŝparkonto, kreditkarto). Por la kuranta konto kaj la ŝparkonto mi povas elekti ajnan periodon ene de la lastaj tri monatoj, sed por la kreditkarto mi povas elekti nur laŭ pagperiodo (t.e. ekde la mezo de unu monato ĝis la mezo de la sekva).
- Alinomi la dosierojn. La nomoj de la elŝutitaj dosieroj konsistas el la elŝutodato (ne la dato(j) de la elektita periodo) kaj la kontonumero, sed tiutempe hledger postulis ke la CSV-dosieroj havu nomon kun sama komenco kiel la dosiero kun kategorireguloj, do mi devis permane ŝanĝi la nomon de la dosieroj.
- Transformi la dosierojn al hledger-formato.
- Enmeti la transformitajn transakciojn en la ĝeneralajn dosierojn, kiuj enhavas transakciojn el pasintaj periodoj. Necesas atenti ke neniu transakcio aperu dufoje en la ĝenerala dosiero. Tio povus okazi se mi malprecize elektis la elŝutperiodojn.
Tiu procedo ja estas komplika, kaj enhavas permanajn paŝojn kiuj estas tedaj kaj erariĝemaj, do mi ekserĉis alian programon por spuri spezojn.
(Se vi ne deziras legi pri la diversaj programoj kiujn mi provis, tuj saltu al la konkludo.)
Gnucash
En pli frua epoko mi uzis Gnucash. Tiam mi konservis paperajn kvitancojn, permane entajpis ilin en Gnucash, kaj de temp' al tempo bilancis komparante al la banka konteltiro. Ĉi-foje mi tamen deziras sistemon pli aŭtomatan.
Gnucash povas importi QIF-dosierojn (kiajn mi ankaŭ povas elŝuti de la banko). Tamen estis pli malfacile enkategoriigi transakciojn: dum la importado, mi povis doni kategorion al ĉiu unuopa transakcio, kaj supozeble Gnucash iom post iom lernas kiel enkategoriigi transakciojn, sed mankis al mi la definitivaj reguloj de hledger: ekzemple transakcioj pri monmaŝinaj elprenoj aperas en la konteltiroj kun la teksto "CASH" sekvata de la adreso de la monmaŝino, kaj sekve mi volus ke ĉiuj transakcioj kun tia priskribo aperu en unu kategorio, sed en Gnucash mi ŝajne devus fidi la lernemon de la programo.
Post la importado, mi elserĉis ĉiujn transakciojn kun tia priskribo, kaj ekhavis liston. En tiu listo mi provis samtempe ŝanĝi la kategorion de ĉiuj transakcioj, sed tio ne eblis — en Gnucash entute ne eblas ŝanĝi pli ol unu transakcion per unu paŝo, kio estas granda malavantaĝo kompare al la situacio en hledger.
Do mi decidis provi aliajn programojn antaŭ ol findecidi kion uzi.
Skrooge
Mi instalis Skrooge kaj donis al ĝi miajn QIF-dosierojn. Skrooge ŝajnis pli promesplena rilate al aŭtomatigo. Ĝi permesas krei regulojn, kun kondiĉoj kaj agoj, kiuj aŭtomate aplikiĝas al transakcioj. Tio estas farata per grafika interfaco, kaj postulas sufiĉe multe da musklakado, sed ĉar sufiĉas tion fari unu fojon, mi imagas ke ne estas tiom terure. (En hledger, mi ofte aldonis novajn regulojn kiam aperis transakcio ne kovrata de jam ekzistanta regulo.)
Resume, la programo ŝajnis sufiĉe taŭga. La interfaco estas plena je funkcioj, por kiujn lerni oni ŝajne bezonas multe da tempo, sed principe tio estas bona afero.
Ekaperis grava manko nur kiam mi faris duan importadon. La programo aŭtomate metas transakciojn en konton kun nomo de la importata dosiero, do mi jam ŝanĝis la nomojn de la kontoj al senchavaj nomoj. Tamen, je la dua importado mi denove havis kontojn kun nurciferaj nomoj, kaj mi tute ne povis trovi manieron movi la transakciojn al jam ekzistantaj dosieroj. Tiu nefleksebleco ĝenis min, do mi daŭrigis mian serĉadon.
KMyMoney
Venis la vico de KMyMoney. Ĝi sen plendoj importis miajn QIF-dosierojn, sed kiom ajn mi serĉis, mi ne sukcesis krei kategoriajn regulojn por importataj transakcioj. Verŝajne la programo ne celas mian uzostilon, sed tiun de malpli pigraj homoj kiuj mem entajpas siajn transakciojn, kaj poste elŝutas konteltiron por certigi ke la banko konsentas kun ili.
Reen al hledger
La rezulto de mia esplorado konfuzis min. Mi volis foriri de mia antaŭa sistemo pro tio, ke ĝi estas tro komplika kaj postulas tro da permana traktado — ĝi aspektas kiel sistemo farita de "nerdo" por "nerdo". Tiuj ecoj igis ĝin tro malfacile uzebla por ke mi havu la emon daŭre enmeti novajn konteltirojn. Mi ankaŭ tion timis, ke mia sistemo eble estis tro ligita al la komputilo en kiu ĝi rulis, tiel ke estus malfacile movi ĝin al alia komputilo se bezonatus. Tial mi serĉis programon celatan al pli vasta celgrupo, sed tio kion mi trovis ne kontentigis min.
Do mi decidis resti ĉe hledger, kaj pliefikigi mian uzon de ĝi:
- Mi kreis skripton kiu alinomas elŝutitajn dosierojn laŭ la datoj de la entenataj transakcioj. Tiel mi povas facile konservi sinsekvajn konteltirojn en unu dosierujo, kaj per unu rigardo vidi kiujn konteltirojn mi havas kaj kiujn ne.
- Mi kreis skripton kiu konvertas la elŝutitajn dosierojn al dosieroj laŭ la formato de hledger. (hledger intertempe jam permesas ke la elŝutita konteltirdosiero kaj la regulardosiero havu diversajn nomojn.)
- Mi kreis skripton kiu kreas dosieron kiu referencas ĉiujn konteltirojn en la dosierujo, tiel ke mi ne plu bezonas mem kunglui la sinsekvajn konteltirojn.
Ankoraŭ la alinomado estas iom malglata; necesas permane alinomi la elŝutitan konteltirdosieron, kaj poste permane ruligi la alinomadan skripton. Mi tamen esperas ke la ĝisnunaj faciligoj povos venki mian pigrecon kaj inspiri min daŭre elŝuti kaj enkategoriigi transakciojn, tiel ke mi havos ĉiujn haveblajn informojn pri kien iris la mono.
De temp' al tempo mi vidis ĉe Guglo serĉrezultojn apud kiuj aperas la nomo kaj foto de la aŭtoro. Tio ekinteresis min pro du kialoj. Unue, mi interesiĝas pri kiel komputiloj (ĉi-kaze la serĉilo de Guglo) "legas" retpaĝojn, eltiras el amaso de teksto informojn por uzi en struktura maniero. Iusence la komputilo "komprenas" simplan paĝon de teksto, kvankam tio principe devus postuli veran inteligentecon. Due — pro vanteco, kompreneble.
Mi esploris pri kiel igi mian retejon aperi en serĉrezultoj kun mia nomo kaj foto, kaj rapide trovis ke tiu funkcio estas forte ligita al profiloj ĉe Google+. Tio iom malemigis min pri la afero. Mi efektive havas profilon tie, sed ne tre ŝatas kiel Guglo+ insistas pri "veraj nomoj", kaj malmulte uzis la servon.
Do, tio devigis min pensi pri la prezo de vanteco ☺
Mi eklegis paĝon ĉe "Retejestraj iloj" de Guglo kiu priskribas kiel fari. Ĝi proponas du opciojn: unue, konekti retpoŝtadreson ĉe la domajno de la retejo kaj mencii la nomon de la aŭtoro en ĉiu paĝo — mi provis tion, sed ne sukcesis igi la testilon rekoni mian nomon — due, krei ligilon al mia profilo ĉe Google+ en ĉiu paĝo kiu aperu kun nomo kaj bildo en serĉrezultoj.
La dua opcio ja kontraŭus la ĝenerale minimumisman aspekton de mia retejo, do mi kreis tian ligilon nur en la ĉefpaĝo. Kaj kelkajn tagojn poste aperis sukceso:
Do, mi ricevis mian vantaĵon, kaj Guglo ricevis ligilon al sia profilservilo. Kiu venkis?…
Mi esploris pri kiel igi mian retejon aperi en serĉrezultoj kun mia nomo kaj foto…
Iuj homoj ŝatas legi sian retpoŝton en Emakso. Kompreneble, Emakso inkluzivas ne nur unu sed du retpoŝtlegilojn, el kiuj la plej funkciriĉa estas Gnus. Uzante Gnus vi povas elekti inter pluraj diversaj formatoj por konservi retpoŝton en la loka disko. Eble vi volas uzi offlineimap por elŝuti retpoŝton el IMAP-servilo, kaj ĉar offlineimap uzas la formaton Maildir, vi eble imagus ke la modulo nnmaildir estas ĝuste tio kion vi serĉas.
Tamen, jam estas dokumentite ke ekzistas kelkaj problemoj pri nnmaildir. Precipe, nnmaildir kaj offlineimap ne konsentas pri kiel marki mesaĝon legita. Normale, offlineimap intersamigas tiajn markojn inter la servilo kaj via loka mesaĝujo, tiel ke mesaĝo kiun vi legis en unu loko fine estos markita kiel legita ankaŭ en la alia loko, sed ĉi-kaze ni malbonsortas; oni devus marki malnovajn mesaĝojn legitaj en ambaŭ lokoj, kio pli-malpli sensencigas la intersamigadon de offlineimap.
Alveninte al tiu punkto, plej multaj homoj rezignas pri nnmaildir kaj instalas en sian komputilon Dovecot, malgranda IMAP-servilo, igas ĝin servi la Maildir-dosierujon per IMAP, kaj konektas Gnus al ĝi per la modulo nnimap. Tio estas tre racia solvo, kaj verŝajne la plej sendolora.
Kompreneble, per tia solvo oni maltrafas ŝancon atingi eternan famon
per skribado de kodo, do mi ekfosis en nnmaildir.el
. Mi trovis ke
ĝi ne atentas tion kion diras la specifo de Maildir
pri mesaĝomarkoj, sed konservas markojn laŭ formato propra al Gnus en
la Maildir-dosierujo. Tiu formato restos bezonata, ĉar ne ĉiuj
markoj, kiujn uzas Gnus, estas prezenteblaj per Maildir-markoj, sed la
plej gravaj ("legita", "respondita" kaj "atentenda") povas esti
konservitaj en parto de la dosiernomoj de la mesaĝoj. Mi verkis
flikaĵon kaj afiŝis ĝin en la cimraporto pri
tiu ĉi funkcio. Ekde 2012-09-05 ĝi estas inkluzivita en la ĉefa
branĉo de Gnus, kaj la ŝanĝo aperis en GNU Emacs 24.3.
Do, jen ĝi, preta por testado. Ŝajne estas iuj problemetoj, tiel ke ĝi iam ne markas mesaĝon legita kvankam ĝi devus, sed ĝis nun ĝi ŝajne ne kutimas manĝi ies retpoŝton.
Ne sufiĉe rapida
Restas pliaj farindaĵoj pri nnmaildir; ĝis nun miaj ŝanĝoj nur celis
korektan funkciadon, do la sekva paŝo estas igi ĝin rapida. Ĝi
funkcias sufiĉe bone por retpoŝtujoj kun miloj da mesaĝoj, sed kiam
temas pri dekmiloj startado de Gnus longe daŭras. (La logika solvo
por tio estas neniam eliri el Gnus, sed…) Mi ankoraŭ ne tute certas
pri ĉio ĉi, sed mi pudris informmesaĝojn sur la kodon kaj ĝis nun ili
sugestas ke la plejparto de la tempo uziĝas en
nnmaildir--grp-add-art
. Se mi ĝuste legas tiun kodon,
ĝi ŝajne enmetas ĉiun artikolon, unu post alia, en laŭordan liston
nomatan nlist
. Evidente, en la plej malbona kazo tio havas
kvadratan tempokomplikecon. Miaj pensoj ĉi-momente estas jenaj:
- Ĉu tio ĉi bezonas esti laŭorda listo? (Aliaj partoj de la kodo faras operaciojn je laŭvicaj partoj de tiu ĉi listo, sed eble ni povus uzi arbon aŭ ion tian…)
- Ĉu ni vere bezonas teni ĉion ĉi en la memoro? Temas pri datumoj pri ĉiu mesaĝo en la dosierujo; plej verŝajne ni la plejmulton neniam uzos.
Jen la nuna stato. Mi plu laboros pri tio ĉi, dum liberaj tempoj.
De kiu vi lernis Esperanton? Kaj de kiu lernis tiu persono Esperanton? Kaj de kiu…
Mi ekhavis ideon, ke estus interese "spuri" kiel scio de Esperanto iris tra homoj dum la historio, kaj tiel krei kvazaŭ parencaran arbon. Kompreneble ekzistas malklaraj kazoj — mi mem eklernis Esperanton per letera kurso enretigita; ĉu en tiu kazo oni konsideru la verkinton de tiu letera kurso mia "instruisto", aŭ ĉu mi estas "memlerninta"? Kaj tiel plu.
Ĉu iu simila esploro jam ekzistas?
De kiu vi lernis Esperanton? Kaj de kiu lernis tiu persono Esperanton? Kaj de kiu…