Planeta Blogów WMI UAM

July 14, 2025

Borkowski Marcin

2025-07-14 Interacting with an unreliable process

Previously, we learned how to send some input to an external process via its stdin and receive its output from its stdout. The process, however, was simple and very reliable. In the real world, an external process can take its time (especially if it uses a database or a network to operate), die or even hang, and it’s good to support such cases.

Let’s start with creating a very simple program which can simulate such an unreliable process. Here is a short Node.js script which does what I want. Notice the readline module which is great for purposes like that. Also, the check for Math.random() being less than a half and calling sleep if it is means that half of the time, the last part of output will include the prompt and half the time the prompt will effectively come in a seperate batch.

#!/bin/env node

import readline from 'node:readline';
import {setTimeout as sleep} from 'node:timers/promises';

const output = JSON.stringify({a: 1, b: [2, 3], c: {d: 4, e: 5}}, null, 4).split('\n');
async function print_lines_slowly(output) {
        for (const line of output) {
                await sleep(400 * Math.random());
                console.log(line);
        }
}

async function main() {
        const rl = readline.createInterface({
                input: process.stdin,
                output: process.stdout,
                terminal: false,
        });

        console.log('commands: json, bye, part, die, hang');
        rl.prompt();

        rl.on('line', async(line) => {
                if (line === 'json') {
                        await print_lines_slowly(output);
                        if (Math.random() < 0.5) {
                                await sleep(400 * Math.random());
                        }
                        rl.prompt();
                } else if (line === 'bye') {
                        rl.close();
                        console.log('Bye!');
                        return;
                } else if (line === 'part') {
                        await print_lines_slowly(output.slice(0, 4));
                        if (Math.random() < 0.5) {
                                await sleep(400 * Math.random());
                        }
                        rl.prompt();
                } else if (line === 'die') {
                        await print_lines_slowly(output.slice(0, 4));
                        process.exit(1);
                } else if (line === 'hang') {
                        await print_lines_slowly(output.slice(0, 4));
                        rl.close();
                        setInterval(() => {}, 1 << 30);
                } else {
                        console.log('command unrecognized');
                        if (Math.random() < 0.5) {
                                await sleep(400 * Math.random());
                        }
                        rl.prompt();
                }
        });
}

main();

Now that we have this program, let’s write some Elisp to drive it. We’ll reuse the code from the previous part, but we’ll have to modify it rather heavily. It has to deal with several cases, and also handle the prompt. Luckily for us, the prompt can never be mistaken for part of the output, so we can only have these cases:

  • the output is a valid JSON plus the prompt, or
  • the output is the word “Bye!” and the process exits cleanly, or
  • the output is a partial JSON plus the prompt, or
  • the output is a partial JSON and the process dies, or
  • the output is a partial JSON and the process hangs, or
  • the output is the phrase “command unrecognized” plus the prompt.
    It is fully conceivable to have a process which can do one or two other things (for example, print a valid response and die then), but the above cases cover enough for me right now.

Note that if we interacted with something with more free-form output (like a shell), where normal output can be identical to a prompt, things would probably get pretty complicated, and frankly, I would be a bit afraid to even look for support for that in (for example) Comint mode – it is well possible there is some support for that, and if I have to choose between diving into that or preserving my sanity, the choice is obvious.

Well, I lied. Or rather, had a change of mind. It dawned on me that it is really easy to check if Comint mode has support like this. I fired M-x shell and said

echo -n "$ " ; sleep 2; echo "now I'm done!"

and immediately felt better for not wanting to support cases like this – neither does Comint mode! It turned out that he above trick actually made it behave as the output from my code was a prompt. But then I noticed that so does

echo -n "and... " ; sleep 2; echo "now I'm done!"

and I realized that it is not even obvious what it could mean to “support cases like this” – after all, what the “correct” behavior would be?

Anyway, here is the plan. Instead of the process filter checking whether the output is a valid, complete JSON, it will just insert the output and look for the prompt – if it is not present, it’ll just wait. And if it is, we’ll check for valid JSON in the case the external program is buggy. At the same time, we are going to detect if the process died and restart it then (using a process sentinel). This approach will cover all of our scenarios except the “hanging” one. For that one, let’s set up a timer and just restart the process if it does not output anything for a specified amount of time.

So, let’s get coding!

(defvar wonky-process--process nil
  "The wonky process.")

(defvar wonky-process--auto-restart-p t
  "Non-nil means restart the wonky process after it dies.")

(defun wonky-process--sentinel (process event)
  "Restart the wonky process if it is dead."
  (when (and wonky-process--auto-restart-p
             (not (process-live-p wonky-process--process)))
    (message "Wonky process died, restarting.")
    (wonky-start-process)))

(defun wonky-start-process ()
  "Start `wonky.js' in the background unless it's already started."
  (interactive)
  (unless (process-live-p wonky-process--process)
    (setq wonky-process--auto-restart-p t)
    (setq wonky-process--process
          (start-process "wonky"
                         (get-buffer-create "*wonky*")
                         "wonky.js"))
    (set-marker-insertion-type (process-mark wonky-process--process) t)
    (setq wonky-process--output-start
          (copy-marker
           (process-mark wonky-process--process)))
    (set-process-filter wonky-process--process
                        #'wonky-process-filter)
    (set-process-sentinel wonky-process--process
                          #'wonky-process--sentinel)))

(defun wonky-stop-process ()
  "Stop the wonky process and do not restart it."
  (interactive)
  (setq wonky-process--auto-restart-p nil)
  (kill-process wonky-process--process))

(defun wonky-process--start-countdown ()
  "Start countdown to restart the wonky process when in case it hangs."
  (wonky-process--stop-countdown)
  (setq wonky-process--timeout-timer
        (run-with-timer wonky-process-timeout
                        nil
                        #'wonky-process--restart-process)))

(defun wonky-process--stop-countdown ()
  "Stop the countdown started by `wonky-process--start-countdown'."
  (when (timerp wonky-process--timeout-timer)
    (cancel-timer wonky-process--timeout-timer)))

(defun wonky-send-input (wonky-input)
  "Send INPUT to the wonky process."
  (interactive "sinput: \n")
  (if (process-live-p wonky-process--process)
      (with-current-buffer (process-buffer wonky-process--process)
        (goto-char (process-mark wonky-process--process))
        (insert wonky-input "\n")
        (setq wonky-process--output-start (point-marker))
        (process-send-string wonky-process--process
                             (concat wonky-input "\n"))
        (wonky-process--start-countdown))
    (user-error "wonky process is not alive")))

(defvar wonky-process--output-start nil
  "The place the current output should start.")

(defcustom wonky-process-timeout 2
  "The time (in seconds) to wait for output from `wonky.js'.")

(defvar wonky-process--timeout-timer nil
  "The timer used to restart the wonky process when it times out.")

(defun wonky-process--restart-process ()
  "Restart the wonky process.
It does so by just killing the process; the actual restarting is handled
by the sentinel."
  (when (processp wonky-process--process)
    (kill-process wonky-process--process)))

(defun wonky-process-filter (process output)
  "Insert OUTPUT to the buffer of PROCESS.
Also, message the user."
  (let ((buffer (process-buffer process)))
    (when (buffer-live-p buffer)
      (with-current-buffer buffer
        (save-excursion
          (goto-char (process-mark process))
          (insert output))
        (save-excursion
          (goto-char wonky-process--output-start)
          (if-let* ((output-end
                     (and (re-search-forward "^> $" nil t)
                          (match-beginning 0))))
              (progn (goto-char wonky-process--output-start)
                     (condition-case error
                         (unless (looking-at "commands:")
                           (message "%s" (json-parse-buffer)))
                       (json-parse-error
                        (message "invalid JSON received: %s"
                                 (buffer-substring-no-properties
                                  wonky-process--output-start
                                  (1- output-end)))))
                     (setq wonky-process--output-start
                           (1+ (match-end 0)))
                     (setq mark wonky-process--output-start)
                     (wonky-process--stop-countdown))
            (wonky-process--start-countdown)))))))

As you can see, this code, while not extremely complex, is not trivial, either. It is still not ideal – for example, if the “wonky” process dies in the middle of producing an output, that partial output is lost (and is not even displayed as “invalid JSON”). Even worse, the same would happen if it died after writing the complete JSON, but before displaying the prompt. It would probably be not very difficult to fix that, but let’s be honest – it’s really an edge-case. Still, it seems to handle the most important cases pretty well: it can parse the results even as they don’t come all at once, it restarts the process if it dies, it can handle the process displaying invalid JSON and prompting again, and it even restarts the process when it hangs (after waiting for some time). It seems it’s all I need! And in two weeks I’m going to show a real tool which does something actually useful and is driven via readline. so we’re getting closer to applying all the knowledge we’ve gathered so far.

CategoryEnglish, CategoryBlog, CategoryEmacs

July 14, 2025 06:00 AM

July 07, 2025

Borkowski Marcin

2025-07-07 Mass resetting Org mode checkboxes

I’m a big fan of checklists. There are many cases where I need to follow some procedure containing many steps, and I really do not want to forget about any of them. This is especially challenging when it is something I need to do not very often, say, once a month, since it is very easy to forget about one or more steps, and properly memorizing the procedure would take many months. One example is preparing and sending invoices, which I need to do at the beginning of every month. Another (slightly less typical) example is packing for vacations – it’s very easy to forget to take something, and very important not to. In fact, I prepared a generic list of things to take with me on vacation more than fifteen years ago, and I use it several times a year, sometimes adding some new item, sometimes removing some no longer needed item.

Me being me, I keep these checklists in Org mode, and they consist of one or more headings with a bunch of checkboxed lists.

One thing which was always a nuisance for me was resetting those checkboxes back to “not checked” state. I usually just query-replace​d the string [X] to [ ], but then I had to manually reset the checkbox cookies on each headline. Finally, I decided that there must be a better method – why not write a simple piece of Elisp to reset all checkboxes for me? Of course, I started with doing a bit of research to make sure I don’t reinvent the wheel. Lo and behold, Org mode already has a feature like that! There is a command called org-reset-checkbox-state-subtree, which resets all checkboxes in the current subtree. Now, the only thing left to automate is to call this function for every subtree in the buffer or region.

(defun org-reset-checkbox-state-buffer ()
  "Reset all checkboxes in the (narrowed portion of) the buffer."
  (interactive "*")
  (org-map-entries #'org-reset-checkbox-state-subtree
                   t nil
                   'archive 'comment))

It’s not ideal – for example, it apparently unfolds the headlines in the whole buffer. Also, it calls (org-update-checkbox-count-maybe 'all), which scans the whole buffer after processing every headline – definitely not optimal. However, I’m not going to run this on Org files larger than, say, several kilobytes (and I do have Org files larger than that – in fact, the very file I’m writing this blog in is over 1.5MB!). Also, if the heading structure is nested, it is enough to run org-reset-checkbox-state-subtree on every level one headline, while my code does that on all subheadings, too – but then again, my packing list (currently the only use-case for this command over org-reset-checkbox-state-subtree) does not have any subheadings at all. If your use-case is more complex, feel free to adapt my code, for example calling org-forward-heading-same-level in a while loop.

That’s it for today, see you next time!

CategoryEnglish, CategoryBlog, CategoryEmacs

July 07, 2025 04:00 PM

June 30, 2025

Borkowski Marcin

2025-06-30 Interacting with an external process via stdin and stdout

For a project I’ve been recently working on, I needed Emacs to interact with an external process, sending things to it via its stdin and receiving responses via its stdout. I never did anything like that before, so I started with checking my options. I heard good things about Comint mode, but after poking around I decided that it’s not what I’m looking for:
[…] Comint mode, a general-purpose mode for communicating with interactive subprocesses.
Since I explicitly do not want the user to interact with the external process, Comint mode is not suitable for this project.

I asked on the Emacs mailing list, and got several helpful answers; again, Eli Zaretskii turned out to be a helpful hero he is.

So, let’s first code a toy example to see how this is done; in the near future, I’m going to show a real-world application. Let’s forget that Emacs has an insanely advanced scientific calculator built in and make a simple interface to bc.

We need (at the very least) to do three things: start the bc process, learn to send requests to it, and accept its responses. (To make things simpler, we will associate a buffer with the process so that all bc responses will be gathered there.) Optionally, we can also restart it when it dies.

(defvar bc-process nil
  "The `bc' process.")

(defun bc-start-process ()
  "Start `bc' in the background unless it's already started."
  (interactive)
  (unless (process-live-p bc-process)
    (setq bc-process (start-process "bc" (get-buffer-create "*bc*") "bc" "-q"))))

So far, it’s pretty clear – we define a variable to store the bc process and create a command to start it (but do nothing if it is already alive).

Let’s make a command to send user-provided input to bc. Note that we need to explicitly add a newline to the string sent.

(defun bc-send-input (bc-input)
  "Send INPUT to `bc-process'."
  (interactive "sbc input: \n")
  (if (process-live-p bc-process)
      (process-send-string bc-process (concat bc-input "\n"))
    (user-error "`bc' process is not alive")))

Note that bc-send-input has one drawback – it asks for the input first and only then makes a check for the aliveness of the bc process. This makes it simpler; a fix for that would include using a Lisp expression instead of a string in the interactive clause, probably also introducing a dedicated history variable etc. No need to do that in this simple example/proof of concept.

And now the fun part comes. So far, we can manually switch to the *bc* buffer to see bc​’s reponses – but I’d like to display them in the echo area instead. To achieve that, we need a process filter. It is a function which is called whenever a process sends something to Emacs. By default, internal-default-process-filter is used, which just inserts the process’ output to its buffer. We want to do the same and to show said output in the echo area. This gets complicated because the process filter may receive partial output. In case of bc this seems unlikely, but for processes performing more complicated tasks it is possible. (In a future article we will swap bc for something with more complex output which may indeed come in several parts, and then we’ll be able to really see this phenomenon.) Basically, we need to track two places in the process buffer. One is where we finished processing the previous output – at the same time, it marks the beginning of the new one. The other is where we finished inserting the “current” output – it marks the place we want to insert its next portion. My first idea was that the former one can be handled by the point, and the latter one by process-mark. After some experiments I discovered that the point behaves strangely when the process buffer is visible (and here’s the reason of why it does so), so I decided to use a dedicated marker variable for that in case the user actually looks at the process buffer.

Here is what I’m going to do. The process filter will first insert the received text into the process buffer at process-mark (like internal-default-process-filter), then check if the newly inserted material is “complete” (in other words, if there is a newline between bc-process--previous-mark and process-mark), and if so, it will gather it, strip the trailing newline, show it in the echo area, and finally move the “previous marker” in the process filter past said newline.

(defvar bc-process--last-mark nil
  "The end of the previously processed result.")

(defun bc-process-filter (process output)
  "Insert OUTPUT to the buffer of PROCESS.
Also, message the user."
  (let ((buffer (process-buffer process))
        (mark (process-mark process)))
    (when (buffer-live-p buffer)
      (with-current-buffer buffer
        (save-excursion
          (goto-char mark)
          (insert output)
          (set-marker mark (point)))
        (when (save-excursion (search-forward "\n" mark t))
          (message "%s" (string-trim-right
                         (buffer-substring bc-process--last-mark
                                           mark)))
          (set-marker bc-process--last-mark mark))))))

(defun bc-start-process ()
  "Start `bc' in the background unless it's already started."
  (interactive)
  (unless (process-live-p bc-process)
    (setq bc-process (start-process "bc"
                                    (get-buffer-create "*bc*")
                                    "bc" "-q"))
    (setq bc-process--last-mark (copy-marker
                                 (process-mark bc-process)))
    (set-process-filter bc-process #'bc-process-filter)))

This seems to work fine, but bc is a very simple program. What if the output from the process can be multiline (well, technically bc can also output many lines at once, but I just ignored that fact for the sake of simplicity), or arrive in parts, etc.? Well, in the next part we’ll create a simple program which accepts a line of input and outputs a (possibly multiline) JSON object, a line at a time, with some delay, and can even hang or die in the middle of producing an output (so that we can make sure that our Elisp code is capable of handling such errors). For now, that’s all!

CategoryEnglish, CategoryBlog, CategoryEmacs

June 30, 2025 04:00 PM

June 23, 2025

Borkowski Marcin

2025-06-23 Making functions interactive

I have to admit that I probably have a bit of OCD, and when I finish my work, I like to put my Emacs in a sort of “clean slate” state. By that I don’t mean closing it or killing all buffers (although when I leave the office, I do kill all work-related buffers). Instead, I mean going back to displaying just one window with the *GNU Emacs* buffer (the one with the logo and links to the tutorial etc.) The problem is, I sometimes accidentally kill that buffer, and then I have no place to go back to;-).

Well, some time ago it occurred to me that something in Emacs must create that buffer, and I can find that something and learn how to do that whenever I want. Grepping for a few words on that splash screen I quickly found the fancy-startup-screen function which does exactly that. I was a bit surprised that it is not an interactive function (IOW, a command), but – as I learned several years ago – this is easy to fix:

;; warning: this does not work!
(put 'fancy-startup-screen 'interactive-form '(interactive))

Sadly, the interactive-form symbol property no longer works. As Stefan Monnier pointed out in that thread, there is another (perhaps cleaner) way to make a non-interactive function into a command:

(advice-add 'fancy-startup-screen
            :before
            (lambda () (interactive) nil))

This method is a bit more verbose, but is clean, the intent is obvious and – last but not least – it works. What else could you want?

CategoryEnglish, CategoryBlog, CategoryEmacs

June 23, 2025 07:00 AM

June 14, 2025

Borkowski Marcin

2025-06-14 Automatically converting timestamps in Emacs

This is a third post in a short series of posts about code which helps me deal with numerical timestamps in Emacs. Last time I wrote a function which decides if a number is a Unix timestamp or a JavaScript Date and converts it to a human-readable ISO-8601 timestamp. What would be even more useful is to see the ISO representation of the timestamp at point without even doing anything. Emacs already has support for showing stuff related to the thing at point in the modeline – ElDoc. Several years ago I had some thoughts about using the ElDoc machinery to do something for me, but I found it too complicated then.

Well, either they modified ElDoc to make it easier to program, or I matured as a programmer, or both. It turned out that it took me about 15 minutes to come up with a working prototype, which is extremely cool!

One way to use ElDoc in your own Elisp code is to write a function which accepts a single argument callback, decides if whatever is at point “deserves” printing something in the echo area, and if yes, call the callback with suitable arguments. This function is next added to eldoc-documentation-functions. (Check its docstring to learn the exact syntax of the callback I mentioned above.) Let’s use the code from the previous post and add the following.

(defun timestamp-conversion-eldoc-function (callback)
  "An ElDoc-compatible wrapper around `number-to-iso-8601'."
  (when-let* ((number-at-point (thing-at-point 'number))
              (timestamp (number-to-iso-8601 number-at-point)))
    (funcall callback (cdr timestamp)
             :thing (car timestamp)
             :face 'font-lock-keyword-face)))

(define-minor-mode eldoc-timestamp-conversion-mode
  "Toggle ElDoc Timestamp Conversion mode.
When enabled, this mode causes numbers at point to be displayed as
timestamps in the echo area using ElDoc (which must be enabled, too).
Numbers greater than `maximum-unix-timestamp-for-conversion' are treated
as JavaScript `Date's and the rest as Unix timestamps (seconds since
1970-01-01)."
  :init-value nil
  (if eldoc-timestamp-conversion-mode
      (add-hook 'eldoc-documentation-functions
                #'timestamp-conversion-eldoc-function
                nil t)
    (remove-hook 'eldoc-documentation-functions
                 #'timestamp-conversion-eldoc-function
                 t)))

As you can see, I made a somewhat controversial decision to not have a lighter for this mode – my modeline is very crowded and I really don’t need even more noise there. I use diminish for that, but recently Emacs got a new feature which might make it obsolete for me.

That’s it for today! I’m now going to think what other uses for ElDoc I could have…

CategoryEnglish, CategoryBlog, CategoryEmacs

June 14, 2025 02:00 PM

June 07, 2025

Borkowski Marcin

2025-06-07 Promisified setTimeout

For quite a few years, when I needed my Node.js scripts to wait for some time, I used this promisified version of setTimeout:

const sleep = milliseconds =>
        new Promise(resolve => setTimeout(resolve, milliseconds))

Having that function, I can now say await sleep(1000) to make my script wait a second.

Of course, I could also use util.promisify for that:

import {promisify} from 'util'
const sleep = promisify(setTimeout)

which is a bit shorter and has a very similar effect.

Recently, however, I learned that there is an even shorter way of defining an equivalent sleep function:

import {setTimeout as sleep} from 'node:timers/promises'

and that’s it! Go check the docs (linked above) to learn about built-in promisified version of setInterval and a few other utilities in the timers/promises module.

CategoryEnglish, CategoryBlog, CategoryJavaScript

June 07, 2025 06:00 PM

May 31, 2025

Borkowski Marcin

2025-05-31 Converting integers to ISO-8601 timestamps

Two weeks ago I wrote about using defcustom​’s :get and :set keywords, allowing the user to set an option containing a Unix timestamp (that is, a number) using an ISO-8601 timestamp. Today I am going to use such an option.

One of the most problematic things in IT is dealing with time in various aspects. Code whose behavior is time-dependent is often hard to write, test and debug. Timezones and DST are a complete mess. The topic of this post – different representations of time — is not the most difficult problem, but it’s still annoying. While I more or less learned to parse (I mean with my eyes, not in code) the American format of date (M/D/Y), I personally use ISO-8601 (YYYY-MM-DD) even though it’s not the the one in use here in Poland (where DD.MM.YYYY is the most widespread). However, Unix timestamps (as the number of seconds since the “Unix epoch”, that is, 1970-01-01) or JavaScript Date​s (as the number of seconds since the same epoch) still defeat my skills. (Note: it is perhaps more correct to say ECMAScript than JavaScript, but come on – who actually says “ECMAScript”?) I can usually guess that an integer represents a timestamp (from the number of digits and the fact that it begins with 17 for current-ish times), I have no idea whether it’s in the past or in the future, not to mention which day (or even year, for that matter) it is.

Of course, the places I most often see timestamps in are the terminal and Emacs, and of course moving stuff from the former to the latter (or even using Emacs as a terminal) is easy to do. So, why not write some Elisp to help me convert the Unix or JS integer timestamps to a more human-friendly representation?

(defun unix-to-iso-8601 (unix-time &optional time-zone)
  "Convert Unix time to ISO-8601."
  (format-time-string "%FT%T%:z" (seconds-to-time unix-time) time-zone))

(defun jsdate-to-iso-8601 (jsdate &optional time-zone)
  "Convert Unix time to ISO-8601."
  (format-time-string "%FT%T%:z" (seconds-to-time (/ jsdate 1000.0)) time-zone))

Note that by default these functions return the local time, but setting time-zone to t will yield UTC (see the reference for other possible values of time-zone).

Let’s make this even better. How about making Emacs recognize whether a number is a Unix time or a JS Date? I will use he fact that most dates we actually see are more or less near to the current date – for example, 1747884031473 is the time I am writing this as JavaScript Date.now(), and treating it as a Unix time yields 57358-03-29T08:04:33+02 – not a date very probable to be used unless you are a Doctor Who fan. On the other hand, treating 1747884031 as JavaScript Date gives 1970-01-21T06:31:24+01:00 – very close to the Unix epoch (obviously) and supposedly mostly interesting for historians.

So, why not have some “threshold” and treat all numbers higher than it as JS dates, and the rest as Unix timestamps?

(require 'iso8601)
(defcustom maximum-unix-timestamp-for-conversion "2038-01-19T03:14:07Z"
  "Threshold for differentiating between Unix time and JS time.
Integers representing Unix times after this timestamp are treated as JS
times, that is, milliseconds from 1970-01-01."
  :type '(string
          :match (lambda (widget val)
                   (iso8601-valid-p val)))
  :set (lambda (sym val)
         (set sym (float-time (encode-time (decoded-time-set-defaults
                                            (iso8601-parse val))))))
  :get (lambda (sym)
         (format-time-string "%FT%T%:::z"
                             (seconds-to-time (symbol-value sym)))))

(It seemed natural to use 2³¹-1 as the cutoff point, but YMMV.)

Now it only remains to use the defined option.

(defun number-to-iso-8601 (number)
  "Convert NUMBER to ISO-8601.
Treat it as Unix timestamp if less than
`maximum-unix-timestamp-for-conversion' and as JavaScript Date
otherwise."
  (if (< number maximum-unix-timestamp-for-conversion)
      (cons 'unix (unix-to-iso-8601 number))
    (cons 'javascript (jsdate-to-iso-8601 number))))

(defun show-number-at-point-as-timestamp ()
  "Echo the number at point as an ISO-8601 timestamp."
  (interactive)
  (when-let* ((number-at-point (thing-at-point 'number))
              (timestamp (number-to-iso-8601 number-at-point)))
    (message "%s: %s" (car timestamp) (cdr timestamp))))

From now on, I can invoke this command when the point is on a number and see the correct timestamp in the ISO-8601 format for both Unix timestamps and JavaScript Date​s.

CategoryEnglish, CategoryBlog, CategoryEmacs

May 31, 2025 07:00 PM

May 26, 2025

Borkowski Marcin

2025-05-26 Adding directories to EMMS playlist from Dired

Like many of us Emacsers, I do much (if not most) my computering in Emacs. This includes using EMMS as my main media player and Dired as my file manager.

One thing I find myself doing pretty often is adding a bunch of subdirectories in my ~/music directory to my EMMS playlist. I usually used emms-add-directory-tree, but it is not really smooth if I want to apply it to many subdirectories. I thought, “there must be a function which adds the current item – or the marked items – to the playlist”. Lo and behold, I found emms-add-dired, which does exactly that.

Now the issue I have with this command is that it is not bound to any key in Dired mode. That’s fair enough – not everyone uses EMMS – but I’d really prefer to have it on some key. The question is, what key do I bind it to? I don’t want to use RET (and lose dired-open-file) – I still want to be able to enter these directories (they are sometimes nested, so I may want first to get into a directory and then emms-add-dired on some of its subdirectories, for example).

One key binding which seems quite natural is E, normally bound to dired-do-open. By default, it opens the marked files in an external program. While EMMS is hardly an “external program”, my mind associates E with things like “play” (that’s what it does with video files), so I’m going to (slightly) abuse the semantic of dired-do-open and make it add my music directories to the EMMS playlist.

The trouble is, however, that dired-do-open does not support anything like this – it basically opens the marked file(s) with xdg-open (at least on GNU/Linux). But this is Emacs, so I can do whatever I want (and whatever works for me)! I could even rebind E to something completely else. Fortunately, I don’t even have to – I can just advise dired-do-open.

(defcustom dired-emms-music-directory "~/music"
  "The path to the directory with music.")

(defun dired-do-add-to-emms-playlist (dired-do-open &rest args)
  "Add FILES to the EMMS playlist if suitable."
  (if (file-in-directory-p default-directory
                           dired-emms-music-directory)
      (emms-add-dired)
    (apply dired-do-open args)))

(advice-add 'dired-do-open
            :around
            #'dired-do-add-to-emms-playlist)

This code is a bit simplistic – the way it knows whether to call emms-add-dired or dired-do-open is by checking if the current directory is a subdirectory of dired-emms-music-directory, so it can break in a Dired buffer with several directories. Still, calling it with both music directories and some other type of files marked is a rather contrived scenario, so I don’t really care that much.

Of course, there is another way of achieving my goal – creating a shell script calling emacsclient with a snippet of Elisp adding its argument to the playlist, and registering it with xdg-mime. That could work, but it’s faster (and also much more fun) to just use Elisp!

That’s it for today, happy listening to music!

CategoryEnglish, CategoryBlog, CategoryEmacs

May 26, 2025 05:00 PM

May 19, 2025

Borkowski Marcin

2025-05-19 Customization variables which require some computation after being set

Today I’d like to learn – and then show – how to define a user option in Emacs so that setting it will trigger evaluating some Elisp code. Here is my use-case. I want the option to be a timestamp. The most convenient way to store it is as Unix time (number of seconds since the epoch), since the code using it will perform comparisons between that timestamp and other times. On the other hand, the most convenient way to set it is as ISO-8601 timestamp, which is inifinitely more human-readable than the Unix time.

It turns out that reading the relevant portion of the manual is not an easy task. I think I get the main idea, but I’m still not sure I understand the difference between :set and :initialize very well, and the fact that the manual says “You have to really understand the workings of Custom to use ‘:get’ correctly” scares me a bit. My first thought was to use :set to convert the ISO-8601 timestamp to Unix time, and :get to convert back, but I’m not sure if this is “using :get correctly”. Well, I asked on the Emacs mailing list, and soon got an answer from Eli himself. I have to admit that I still do not understand everything here, but at least I feel a bit more confident now that :initialize is not what I need here.

So, here is my code. It turned out that the hard portion is actually parsing ISO-8601. Who would have guessed;-)? Emacs has an in-built library for that, but it lacks a function to just parse a string like 2025-05-09 into a proper Elisp time object. The iso8601-parse does almost that, but instead of filling the missing data (the hour, minute and second) with zeroes, it uses nils. That sort of makes sense, but then encode-time does not like it at all. Good thing is that Elisp has another function which does exactly what I need here, that is, turns those nils into zeros – decoded-time-set-defaults.

(require 'iso8601)
(defcustom timestamp-user-option "1970-01-01T00:00+00"
  "An example of a timestamp user option.
This is stored as a number but set in Customize as ISO-8601 string."
  :type '(string
          :match (lambda (widget val)
                   (iso8601-valid-p val)))
  :set (lambda (sym val)
         (set sym (float-time (encode-time (decoded-time-set-defaults
                                            (iso8601-parse val))))))
  :get (lambda (sym)
         (format-time-string "%FT%T%:::z"
                             (seconds-to-time (symbol-value sym)))))

Now you can use customize-option to set this to (for example) 2025-05-19, and the real value of the variable will be 1747605600.0 (at least in my timezone).

One little disadvantage is that when you enter the Customize UI again, you’ll see the full ISO-8601 timestamp, that is, 2025-05-19T00:00:00+02 (again, in my timezone). This could be remedied by changing this variable into a structure (maybe a plist, maybe an alist, maybe an object) and store both the actual timestamp entered by the user and the result of the conversion to a number, but I don’t think it’s worth the hassle, since then you wouldn’t be able to treat this variable as a number – you’d have to use plist-get or some other accessor function. Another idea could be to use timestamp-user-option to store the exact string the user typed and some other variable, say timestamp-user-option-internal to store its numerical representation. This would probably work, but I think it’s even worse, since then both variables can get out of sync with a setq or something.

Anyway, that’s it for today – but expect a follow-up, where we will actually use a user option like this, in the near future!

CategoryEnglish, CategoryBlog, CategoryEmacs

May 19, 2025 06:00 PM

January 08, 2015

Hromada Mateusz

Warka 9 – American India Pale Ale

Zasyp:

  • pale ale 4kg
  • pszeniczny 0,8kg
  • carapils 0,5kg

Zacieranie w 18l wody, słód dodany w 55°C:

  • 68°C – 65 minut przerwy (po 30 minutach podgrzanie z 65°C do 68°C)

Po filtracji i wysładzaniu wyszło 21,5 litra brzeczki o BLG 11,1.

Chmielenie:

  • 60 minut – 40g Simcoe
  • 20 minut – 30g Simcoe
  • 5 minut – 30g Simcoe
  • 0 minut – 30g Amarillo
  • dry hopping – 30g Simcoe (6 dni), 30g Simcoe (3 dni)

Po schłodzeniu i filtracji wyszło 19 litrów o BLG 16.

Fermentacja (drożdże US-05):

  • 11 dni burzliwej
  • 23 dni cichej

Do refermentacji użyłem 140g cukru i 735g wody.

ABV: ok. 6.2%

by ruanda at January 08, 2015 09:38 PM

Warka 8 – Cydr

Składniki:

  • sok jabłkowy Riviva 22l

Sok ma BLG 12.

Fermentacja (drożdże US-05):

  • 7 dni burzliwej
  • 38 dni cichej

Do refermentacji użyłem 155g cukru i 1135g wody.

ABV: ok. 6.3%

by ruanda at January 08, 2015 09:34 PM

Warka 7 – American Wheat

Zasyp:

  • pszeniczny 2kg
  • pilzneński 2kg

Zacieranie w 16l wody, słód dodany w 45°C:

  • 45°C – 15 minut przerwy
  • 53°C – 15 minut przerwy
  • 68°C – 75 minut przerwy (po 30 minutach podgrzanie z 64°C do 70°C)

Po filtracji i wysładzaniu wyszło 21 litry brzeczki o BLG 7.

Chmielenie:

  • 60 minut – 10g Chinook
  • 20 minut – 20g Palisade
  • 5 minut – 20g Cascade
  • 0 minut – 20g Amarillo
  • dry hopping – 30g Amarillo, 10g Cascade, 10g Palisade

Po schłodzeniu i filtracji wyszło 18 litrów o BLG 12.

Fermentacja (drożdże US-05):

  • 8 dni burzliwej
  • 35 dni cichej

Do refermentacji użyłem 120g cukru i 880g wody.

ABV: ok. 5.5%

by ruanda at January 08, 2015 09:28 PM

November 11, 2014

Girl, lost in IT

Nowy wpis, nowe miejsce

W poprzednim wpisie uprzedzałam, że muszę wprowadzić duże zmiany. Nie jestem już w stanie pisać każdego tekstu w dwóch językach. Dodatkowo czuję, że zmieniłam się przez ostatnie kilka lat – czuję, że „wyrosłam” trochę z tego bloga. Właśnie napisałam pierwszy tekst w nowym miejscu. Blog nazywa się Na miękko - serdecznie zapraszam.

by ynka at November 11, 2014 09:02 PM

October 13, 2014

Krzysztof Szarzyński

Zawieszenie publikacji

Jeśli dobrze liczę, to od 402 dni nic tu nie napisałem. Ponieważ zazwyczaj nie mam niczego interesującego do powiedzenie/napisania, dlatego przerzuciłem się na inne platformy, które lepiej pasują do “beztekstowej” formuły. Nadal robię zdjęcia – więcej niż kiedyś, ale publikuje je w mniejszych porcjach. Zapraszam na mój profil 500px, gdzie wrzucam rożne zdjęcia z całego […]

by Quati at October 13, 2014 06:41 PM

June 12, 2014

Hromada Mateusz

Warka 6 – Żytni Stout

Zasyp:

  • pale ale 2kg
  • żytni 2kg
  • żytni czekoladowy 0,25kg
  • Carafa typ III special 0,25kg

Zacieranie w 16l wody, słód dodany w 55°C:

  • 55°C – 10 minut przerwy
  • 70°C – 60 minut przerwy (po 60 minutach podgrzanie z 62°C do 68°C)
  • 68°C – dodanie ciemnych słodów, 40 minut przerwy

Po filtracji i wysładzaniu wyszło 20,5 litra brzeczki.

Chmielenie:

  • 60 minut – 10g Tomahawk
  • 15 minut – 30g Tomahawk
  • 5 minut – 30g Tomahawk
  • dry hopping – 30g Tomahawk

Po schłodzeniu i filtracji wyszło 18 litrów o BLG 13.

Fermentacja (drożdże S-04):

  • 22 dni burzliwej

Do refermentacji użyłem 125g cukru i 835g wody.

ABV: ok. 4,4%

by ruanda at June 12, 2014 08:38 PM

April 27, 2014

Girl, lost in IT

W planie zmiany + jak tu trafiłam (w obrazkach)

Kolega wyraził ostatnio zaniepokojenie działaniem swojego czytnika RSS. To przecież niemożliwe, żebym tak długo nie napisała nic na swoim blogu! Niestety, możliwe. Do tego powód jest dość absurdalny: piszę mniej, ponieważ piszę więcej. Naprawdę. Pamiętacie, jak przeżywałam swoje 10000. urodziny? Postanowiłam wtedy na poważnie wziąć się za rzeczy, o których zawsze myślałam, że zrobię je […]

by ynka at April 27, 2014 06:28 AM

March 22, 2014

Hromada Mateusz

Warka 5 – Imperial India Pale Ale

Zasyp:

  • pale ale 5kg
  • wiedeński 1kg
  • carapils 0,5kg
  • pszeniczny 0,2kg

Zacieranie w 21l wody, słód dodany w 55°C:

  • 55°C – 10 minut przerwy
  • 65°C – 90 minut przerwy (po 60 minutach podgrzanie z 61°C do 65°C)

Po filtracji i wysładzaniu wyszło 23 litry brzeczki o BLG 14.

Chmielenie:

  • 60 minut – 40g Chinook
  • 10 minut – 20g Citra, 20g Simcoe
  • 5 minut – 30g Citra, 30g Simcoe
  • 0 minut – 15g Cascade, 15g Palisade
  • dry hopping – 15g Cascade, 15g Palisade

Po schłodzeniu i filtracji wyszło 21 litrów o BLG 17.

Fermentacja (drożdże US-05):

  • 9 dni burzliwej
  • 10 dni cichej

Do refermentacji użyłem 130g cukru i 630g wody.

ABV: ok. 7,6%

by ruanda at March 22, 2014 07:44 PM

March 12, 2014

Hromada Mateusz

Warka 4 – American Wheat

Zasyp:

  • pszeniczny 2kg
  • pilzneński 2kg

Zacieranie w 15l wody, słód dodany w 55°C:

  • 55°C – 10 minut przerwy
  • 68°C – 70 minut przerwy (po 50 minutach podgrzanie z 62°C do 67°C)

Po filtracji i wysładzaniu wyszło 21 litry brzeczki o BLG 9,5.

Chmielenie:

  • 60 minut – 10g Chinook
  • 20 minut – 20g Palisade
  • 5 minut – 20g Cascade
  • 0 minut – 20g Amarillo
  • dry hopping – 30g Amarillo

Po schłodzeniu i filtracji wyszło 18 litrów o BLG 13.

Fermentacja (drożdże US-05):

  • 8 dni burzliwej
  • 11 dni cichej

Do refermentacji użyłem 120g cukru i 800g wody.

ABV: ok. 5.3%

by ruanda at March 12, 2014 06:14 PM

February 23, 2014

Girl, lost in IT

Oddam za darmo!

Raz na jakiś czas znajduję w domu coś, co dawno już przestało mi być potrzebne, ale co mogłoby jeszcze przydać się komuś innemu. Niektóre takie rzeczy wystawiam na Allegro (zwłaszcza, jeśli są warte więcej niż kilkadziesiąt złotych), jednak do Allegro często zniechęca mnie konieczność oszacowania kosztów przesyłki. Jeśli chcę pozbyć się czegoś szybko, tanio i […]

by ynka at February 23, 2014 05:23 PM

February 15, 2014

Hromada Mateusz

Instalacja Raspbian przez debootstrap

IMG_20140214_203617

Wymagania

Potrzebny jest Linux, w miarę świeży debootstrap i qemu-arm-static. Oczywiście potrzebny też jest dostęp do użytkownika root.

Partycjonowanie i formatowanie karty SD

Kartę należy podzielić na dwie partycje. Pierwsza, sformatowana w FAT będzie zamontowana jako /boot. Druga partycja będzie zamontowana jako /, można ją sformatować np. w ext4:

root@lol:/mnt# fdisk /dev/sdh
WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
switch off the mode (command 'c') and change display units to
sectors (command 'u').

Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1021, default 1):
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-1021, default 1021): +64M

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): c
Changed system type of partition 1 to c (W95 FAT32 (LBA))

Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 2
First cylinder (10-1021, default 10):
Using default value 10
Last cylinder, +cylinders or +size{K,M,G} (10-1021, default 1021):
Using default value 1021

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: If you have created or modified any DOS 6.x
partitions, please see the fdisk manual page for additional
information.
Syncing disks.
root@lol:/mnt# mkfs.vfat /dev/sdh1
[...]
root@lol:/mnt# mkfs.ext4 /dev/sdh2
[...]
root@lol:/mnt# mkdir rpi
root@lol:/mnt# mount /dev/sdh2 rpi/
root@lol:/mnt# mkdir rpi/boot
root@lol:/mnt# mount /dev/sdh1 rpi/boot/

Instalacja i konfiguracja

Debootstrap należy przeprowadzić w dwóch etapach, gdyż binarki przeznaczone są na inną architekturę.

root@lol:/mnt# debootstrap --foreign --arch armhf wheezy rpi/ http://archive.raspbian.org/raspbian
[...]
root@lol:/mnt# cp /usr/bin/qemu-arm-static rpi/usr/bin/
root@lol:/mnt# LANG=C chroot rpi/ /debootstrap/debootstrap --second-stage

Następnie można chrootować się na budowany system:

root@lol:/mnt# LANG=C chroot rpi/ /bin/bash

Potrzebne są repozytoria w sources.list:

deb http://archive.raspbian.org/raspbian wheezy main contrib non-free
deb-src http://archive.raspbian.org/raspbian wheezy main contrib non-free

W pliku /boot/cmdline.txt należy dodać parametry do kernela:

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

/etc/fstab:

proc /proc proc defaults 0 0
/dev/mmcblk0p1 /boot vfat defaults 0 0

Różne sieciowe ustawienia są w plikach /etc/hostname, /etc/resolv.conf i /etc/network/interfaces.

W chroocie należy doinstalować paczki git, binutils i ca-certificates.

Potrzebne jest narzędzie rpi-update:

root@lol:/# wget http://goo.gl/1BOfJ -O /usr/bin/rpi-update
root@lol:/# chmod +x /usr/bin/rpi-update
root@lol:/# mkdir /lib/modules
root@lol:/# rpi-update
[...]

Aby dostać się do RPi po restarcie potrzebne jest ssh i hasło na roota:

root@lol:/# passwd
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@lol:/# apt-get install ssh
[...]

Po wyjściu z chroota, odmontowaniu obu filesystemów i przełożeniu karty do RPi powinien zabootwać się Raspbian dostępny przez ssh:

root@trololo:~# uname -a
Linux trololo 3.10.30+ #640 PREEMPT Fri Feb 14 19:09:14 GMT 2014 armv6l GNU/Linux

Linki

by ruanda at February 15, 2014 01:20 PM

February 02, 2014

Hromada Mateusz

Warka 3 – American Stout

Zasyp:

  • pilzneński 3kg
  • monachijski 1kg
  • jęczmień palony 0,3kg
  • barwiący 0,2kg

Zacieranie w 15l wody, słód dodany w 55°C:

  • 55°C – 10 minut przerwy
  • 68°C – 70 minut przerwy (po 35 minutach podgrzanie z 64°C do 66°C)
  • 66°C – dodanie ciemnych słodów i 15 minut przerwy

Po filtracji i wysładzaniu wyszło 22 litry brzeczki o BLG 11.

Chmielenie:

  • 60 minut – 15g Centennial
  • 15 minut – 15g Centennial
  • 5 minut – 15g Centennial
  • 0 minut – 25g Centennial
  • dry hopping – 30g Centennial

Po schłodzeniu i filtracji wyszło 19,5 litra o BLG 15. Dodałem 1,5l wody.

Fermentacja (drożdże S-04):

  • 8 dni burzliwej
  • 12 dni cichej

Do refermentacji użyłem 140g cukru i 860g wody.

by ruanda at February 02, 2014 09:54 PM

Girl, lost in IT

10000* lat

Parę (naście) dni temu skończyłam 32 lata. Ta magiczna liczba nie doskwiera mi za bardzo i nie ona jest bezpośrednią przyczyną powstania tego tekstu. Do rozmyślań skłoniły mnie życzenia, które składano mi z tej okazji. Kilkoro (młodszych) znajomych, niezależnie od siebie, obdarowało mnie życzeniami brzmiącymi mniej więcej tak: „Wszystkiego najlepszego. Żebyś miała wspaniały rok. I […]

by ynka at February 02, 2014 06:17 PM

January 27, 2014

Hromada Mateusz

Warka 2 – American Pale Ale

Zasyp:

  • pilzneński 2,5kg
  • monachijski 1,3kg
  • karmelowy jasny 0,2kg

Zacieranie w 12l wody, słód dodany w 55°C:

  • 55°C – 10 minut przerwy
  • 69°C – 60 minut przerwy (z podgrzaniem w 40 minucie)

Po filtracji i wysładzaniu BLG 10.

Chmielenie:

  • 60 minut – 15g Simcoe
  • 15 minut – 15g Simcoe
  • 5 minut – 15g Simcoe
  • 0 minut – 25g Simcoe
  • dry hopping – 30g Simcoe

Po schłodzeniu i filtracji wyszło 16,5 litra o BLG 13.

Fermentacja (drożdże US-05):

  • 8 dni burzliwej
  • 10 dni cichej

Do refermentacji użyłem 110g cukru i 740g wody.

ABV: ok. 5%

by ruanda at January 27, 2014 06:25 PM

January 26, 2014

Hromada Mateusz

Warka 1 – American India Pale Ale

Pierwszą warkę uwarzyłem z przygotowanego zestawu surowców.

Zasyp:

  • pale ale 5,5kg
  • Cara Gold 0,5kg
  • pszeniczny 0,2kg

Zacieranie w 18l wody, słód dodany w 55°C:

  • 55°C – 10 minut
  • 69°C – 60 minut (bez podgrzewania, końcowa 62°C)
  • 76°C – mashout

Po filtracji i wysładzaniu wyszło 23 litry brzeczki o BLG 15.

Chmielenie:

  • 60 minut – 25g Warrior
  • 20 minut – 20g Simcoe
  • 10 minut – 10g Amarillo
  • 5 minut – 10g Amarillo
  • dry hopping – 30g Amarillo

Po schłodzeniu i filtracji wyszło 20,5 litra o BLG 17.

Fermentacja (drożdże US-05):

  • 10 dni burzliwej
  • 11 dni fermentacji cichej

Do refermentacji użyłem 135g cukru i 660g wody.

ABV: ok. 7%.

by ruanda at January 26, 2014 07:03 PM

December 15, 2013

Girl, lost in IT

O zmianie (pracy)

Kilka dni temu kolega z poprzedniej pracy spytał, czy przeniosłam blog w nowe miejsce – nie napisałam prawie nic od czasu, gdy zmieniłam pracę. Babcia przy każdym spotkaniu pyta, czy „korporacja” zdążyła już przeżuć mnie i wypluć. Odpowiem krótko: nie i nie! To prawda, że w ciągu ostatnich kilku miesięcy nie napisałam zbyt wiele. Miało […]

by ynka at December 15, 2013 07:46 PM

September 08, 2013

Krzysztof Szarzyński

Warszawa 2013 – Quati w stolicy.

Zanim zaproszę na ostatnią odsłonę zdjęć z mojego krótkiego pobytu w Warszawie, chwila luźnych wywodów. Nocowałem w hotelu przy placu Konstytucji, który oferował dwa rodzaje pokoi: ciche i z widokiem na miasto. Mi się trafiło oglądanie miasta, ale ponieważ ze mną na szkolenie przyjechało kilka innych osób z Poznania, to zdecydowaliśmy się z Adamem iść […]

by Quati at September 08, 2013 06:19 PM

September 04, 2013

Krzysztof Szarzyński

Warszawa 2013 – część druga

Dziś druga porcja obrazków z Warszawy. Hasłem przewodnim miało być “ludzie w Warszawie”, ale chyba troszkę bardziej pasuje “życie w Warszawie”. Chyba nigdy nie byłem dobry w nadawaniu tytułów i nazw. 🙂 Część trzecia zostanie opatrzona moimi przemyśleniami i komentarzem co do samego miasta. Zdjęcia również będą bardziej dotyczyć tego co widziałem i przeżyłem 🙂

by Quati at September 04, 2013 05:27 PM

September 01, 2013

Krzysztof Szarzyński

Warszawa 2013 – część pierwsza

W tym roku wakacje były podzielone na dwie cześći. Najpierw pojechaliśmy z Olą do Zwierzyńca, gdzie odpoczywaliśmy aktywnie zwiedzając Zamojszczyznę i korzystając z dobrodziejstwa krętych rzek i kajaków turystycznych. Ciągle czekam na chwilę weny żeby wybrać kilka zdjęć i je opublikować 🙂  Drugim etapem moich wakacji było… skończenie życia jako student i rozpoczęcie “dorosłości” w […]

by Quati at September 01, 2013 08:07 PM

Girl, lost in IT

Cicho sza!

Wcale nie zarzuciłam pisania tego bloga. Krótko o tym, co ostatnio dzieje się u mnie: Kupiliśmy dom. Trwa remont. Kompletnie nie doszacowałam nakładów czasowych (o finansowych nie będziemy tu rozmawiać) niezbędnych do realizacji takiego przedsięwzięcia. Tłumaczę książkę o C++ i Qt (Dlaczego? Patrz wyżej). Jest gruba. Zamiast tłumaczyć, spędzam godziny na rozmowach z fachowcami. Pół […]

by ynka at September 01, 2013 07:36 PM

July 13, 2013

Girl, lost in IT

Open’er, czyli wieczne poszukiwania

Heineken Open’er to co roku ta sama historia. Wiele tygodni przed festiwalem niecierpliwie zaczynam śledzić lineup i planować, co będę chciała zobaczyć. Bliżej terminu okazuje się, że z pięciu wymarzonych koncertów dwa odbywają się naraz, a trzy inne jeden po drugim – na scenach oddalonych od siebie o kilometr. Planuję także, z kim pojadę i […]

by ynka at July 13, 2013 08:00 AM

June 26, 2013

Krzysztof Szarzyński

Nowe zabawki i noc kupały

Obiecanki – cacanki. Czyli tradycyjnie, jak Quati mówi, że coś napisze, to znaczy, że mówi. 🙂 Dziś mam kilka zdjęć zrobionych za pomocą nowej zabawki, która oprócz tego, że jest świetnym gadżetem, to spełnia też czasami funkcję obiektywu. Otóż moim nabytkiem jest Sigma 50 mm F1.4 DG EX HSM, czyli typowa portretowa “stałka” z dość jasnym […]

by Quati at June 26, 2013 09:43 PM

May 30, 2013

Girl, lost in IT

Feminiści kontra statystyka

Uczestniczyłam ostatnio w konferencji, w której programie znalazł się panel dyskusyjny poświęcony mniejszościom w środowisku informatyków. Podczas panelu miałam okazję porozmawiać z inspirującymi i pełnymi dobrej woli ludźmi, jednak doszło tam również do pewnego zderzenia opinii, które wspominam do tej pory. Był sobie facet. Doświadczony lider, blogger, walczący o zwiększenie liczby kobiet w IT. Osoba, […]

by ynka at May 30, 2013 04:54 PM

April 20, 2013

Girl, lost in IT

Kup swojemu dziecku sobie robota Lego Mindstorms

Pierwszy raz klocki Lego widziałam, będąc małym dzieckiem, nie pamiętam nawet, czy było to Peweksie, „u prywaciarza”, czy może u kolegi, którego tata był kapitanem na statku handlowym, przez co w ich domu zawsze można było trafić na skarby niedostępne w normalnych sklepach (począwszy od mandarynek). Pamiętam, że zachwyciły mnie czyste kolory, przemyślane projekty oraz możliwości, jakie […]

by ynka at April 20, 2013 04:05 PM

March 16, 2013

Girl, lost in IT

Na słodko-gorzko o skutkach asertywności

Minęło już półtora miesiąca od kiedy zmieniłam pracę. Czas… biegnie szybko. Decyzja ciągle wydaje się dobra. Uczę się bardzo dużo. W trakcie ostatnich tygodni dwa razy odwiedziłam Warszawę, po raz pierwszy spróbowałam koreańskiej kuchni (z wyjątkiem zupy kimchi w Art Sushi, którą polecam, ale tylko ludziom o żołądkach ze stali), a także zapisałam się na […]

by ynka at March 16, 2013 12:37 PM

February 24, 2012

Dopierała Andrzej

2011-05-27 Darmowe fotoradary dla nissan connect

Może kogoś zainteresuje - nie znalazłem nigdzie opisu jak to łatwo zrobić “za friko”.

1. Ściągamy listę fotoradarów z http://ump.waw.pl/ plik z mapą fotoradarów (http://ump.waw.pl/update/Jacek.zip).
2. Rozkompresowujemy go
3. Łączymy to co nas interesuje do jednego pliku csv:

undefine@uml:~/tmp$ cat Kontrole\ Drogowe/*.csv Niebezpieczne\ Miejsca/*.csv Przejazdy\ Kolejowe/*.csv >x.csv
undefine@uml:~/tmp$ 

4. Usuwamy polskie znaczki (zapewne można jakoś przekodować, ale nie chciało mi się metodą prób i błędów szukać kodowania):

iconv -f cp1250 -t ascii//TRANSLIT  < x.csv > jacek.csv

5. Plik jacek.csv umieszczamy w katalogu myPOIs/myPOIWarnings na pendrive
6. Pendrive wkładamy do nissan connect i ładujemy POI użytkownika z menu(Setup → Navigation → Download my POIs)
7. Przestrzegamy ograniczeń ;)

February 24, 2012 08:24 AM

December 23, 2011

Dopierała Andrzej

2011-12-23 Ankieta Polskiego Badania Czytelnictwa: Przyszła do mnie prośba o wypełnienie ankiety. Z Polskiego Badania Czytelnictwa, sp. z o.o. Niby nic dziwnego, ale.. przyszła "smailem". A sama ankieta, po podaniu "pinu" z listu do wypełnienia na stronie http://www.ankieta.pbc.pl/ W zamian za wypełnienie ankiety oferują m.in. kupony sodexo na kwotę 20zł. I w sumie... Podszedłem do tematu trochę z mieszanymi uczuciami. Jak? Skąd? Po co? Z tego co znalazłem dane osobowe otrzymali z bazy PESEL na podstawie Dz.U. 2006 nr 139 poz. 993, 44h ust 2 par 2. List - był zaadresowany do mnie (imię, nazwisko, adres). Natomiast w samej ankiecie na stronie zwrot był przez "Pani". Pomyłka? Koniec końców zmarnowałem kwadrans i wypełniłem. Co prawda niezbyt wartościowo, bo z proponowanych czasopism to tylko sporadycznie Metro przeglądam jak akurat mi w korku wcisną, natomiast Fantastyki w ankiecie nie było, ale - jak to badanie statystyczne, to.. czemu nie.

Przyszła do mnie prośba o wypełnienie ankiety.
Z Polskiego Badania Czytelnictwa, sp. z o.o.

Niby nic dziwnego, ale.. przyszła “smailem”. A sama ankieta, po podaniu “pinu” z listu do wypełnienia na stronie http://www.ankieta.pbc.pl/

W zamian za wypełnienie ankiety oferują m.in. kupony sodexo na kwotę 20zł.

I w sumie… Podszedłem do tematu trochę z mieszanymi uczuciami. Jak? Skąd? Po co? Z tego co znalazłem dane osobowe otrzymali z bazy PESEL na podstawie Dz.U. 2006 nr 139 poz. 993, 44h ust 2 par 2.

List - był zaadresowany do mnie (imię, nazwisko, adres). Natomiast w samej ankiecie na stronie zwrot był przez “Pani”. Pomyłka?

Koniec końców zmarnowałem kwadrans i wypełniłem. Co prawda niezbyt wartościowo, bo z proponowanych czasopism to tylko sporadycznie Metro przeglądam jak akurat mi w korku wcisną, natomiast Fantastyki w ankiecie nie było, ale - jak to badanie statystyczne, to.. czemu nie.

December 23, 2011 09:04 PM

November 07, 2011

Dopierała Andrzej

2011-11-07 brother mfc5490CN: drukarka brother mfc 5490cn pod linuksem i.. ipv6.

Stara drukareczka HP 6540 się popsuła (uchwyt od czarnego tuszu przestał kontaktować), naprawa oczywiście nieopłacalna, a mi by się w domu przydał skaner, wiec… Nabyłem nową drukareczkę. Po przejrzeniu dostępnych opcji wybór padł na Brother MFC 5490CN
I - jak na razie nie mam co narzekać.
Drukowanie spod linuksa - ruszyło praktycznie od razu.
Skanowanie spod linuksa - również. Mimo iż po raz pierwszy w życiu konfigurowałem skaner pod linuksem.

A na dokładkę - drukarka działa również po ipv6…

Local> show ip v6

IPv6 is Enabled
IPv6 Static Address is Enabled
IPv6 Addresses are 2A02:848:A::XXXX:XXXX:XXXX:XXXX/64 (via Router)
                   FE80::XXXX:XXXX:XXXX:XXXX/64 (link-local)

The priority IP version of name resolving is IPv4

Będę musiał chyba firewalla wyklikać dla ipv6 :/

November 07, 2011 10:56 PM

September 09, 2011

Ogrodowczyk Wojciech

Koniec tej zabawy

Jak co bystrzejsi czytelnicy mogli zauważyć, nie pisuję już tu praktycznie nic, a zdjęcia tu umieszczane to jedynie opóźnione „przedruki” względem mojego konta na flickr. Kierując się więc brzytwą Okhama i zwykłym lenistwem...

September 09, 2011 08:12 AM

August 22, 2011

Dopierała Andrzej

2011-08-22 Lekko przerobiony multi router looking glass w php: Skrypt do przedstawiania na routerach.

Już kilka osób się mnie pytało jak zrobiłem http://lg.maverick.com.pl/.

Bazuję na lekko zmodyfikowanej wersji MRLG (Multi Router Looking Glass for PHP). Obecnie jego strona niestety nie działa - i oryginału nie mam do pobrania.

Moją zmodyfikowaną wersję umieściłem na mrlg-20091215.tgz, natomiast plik konfiguracyjny do niej na mrlg-config.php. Na pytania nie odpowiadam, w konfiguracji nie pomagam (bo nie mam czasu) - ale może komuś się przyda :).
Licencja - zgodnie z licencja oryginału - GPL.

Ale jak masz jakieś poprawki - podsyłaj :)

Wiem że brzydko zrobione, ale - było potrzebne na szybko i w parę godzin tylko na tyle mnie było stać.

Jak dodasz w swojej sieci kopię - zgłoś na traceroute.org - widoczność swojego miejsca w sieci się często przydaje :).

August 22, 2011 07:55 PM

August 21, 2011

Ogrodowczyk Wojciech

August 11, 2011

Ogrodowczyk Wojciech

August 05, 2011

Dopierała Andrzej

2011-08-05 Problematyczne ipv6 - dziwny resolver: problem z domenami z dodanym rekordem AAAA.

undefine@uml:~$ host aramin.net
aramin.net has address 42.1.94.0
;; Warning: Message parser reports malformed message packet.
;; connection timed out; no servers could be reached

i tak dla sporej części, o ile nie wszystkich adresów z dualstackiem - taki dziwny feature AP DSL-G604T w hotelu.

Do tej pory myślałem że dodanie rekordu AAAA nic nie psuje, teraz - sam się przekonałem że nie jest tak różowo, jak sie okazało że nie mogę się dostać na strony/serwery z dodanym AAAA. Na szczęście - można skorzystać z własnych albo googlowych dnsów.

Oczywiście prawidłowa odpowiedź to

undefine@uml:~$ host aramin.net
aramin.net has address 195.110.48.48
aramin.net has IPv6 address 2a01:5e00:2:52::30
aramin.net mail is handled by 0 mx.aramin.net.

August 05, 2011 04:55 PM

July 29, 2011

Ogrodowczyk Wojciech

July 27, 2011

Ogrodowczyk Wojciech

July 21, 2011

Ogrodowczyk Wojciech

July 14, 2011

Ogrodowczyk Wojciech

June 26, 2011

Dopierała Andrzej

2011-06-26 IPv6 na stronie: ipv6 na stronie

Co prawda po IPv6 day, ale.. wreszcie dołożyłem AAAA do mojej strony(i stron firmowych). Ciekawe co wybuchnie ;)

June 26, 2011 07:24 PM

June 17, 2011

Ogrodowczyk Wojciech

May 31, 2011

Ogrodowczyk Wojciech

Planet shot

Bcn planet shot

Eksperymentale ujęcie wychodka na szczycie świata, krok po...

May 31, 2011 11:44 AM

May 29, 2011

Ogrodowczyk Wojciech

March 12, 2011

Dopierała Andrzej

2011-03-12 IPv6-garsc-statystyk: kilka słów podsumowania o ruchu ipv6

Kilka statystyk z pewnego routerka. Zbierane ip6tables, w dłuższym okresie czasu. Wykresów nie daję, bo za mała próbka by ładnie wyglądało ;)

Dane z pewnej sieci osiedlowej w Poznaniu. IPv6 udostępnione klientom przez radvd. Głównie klienci “prywatni”.

Wejście:
2% - 6to4
pomijalne - PIX
24% - PLIX
3% - natywne IPv6 od Crowleya
69% - tunel z HE

Wyjście:
51% - 6to4(!)
6% - PIX
36% - PLIX
1% - Crowley
6% - tunel z HE.

Wnioski:
- podobnie jak w przypadku ipv4 duża część ruchu przypada na PLIX
- upload to głównie ruch kierowany do 6to4 - jak sądzę ruch p2p.
- bardzo duży download na tunelu z HE. Co prawda miałem trochę wątpliwości czy należy się w to bawić - to jednak obserwując ruch przy włączonym i przy wyłączonym tunelu widać że opóźnienia przez HE są często niższe. Pozatym - alternatywa to pchanie ruchu przez Crowleya, który.. również sporą część ruchu ma przez HE. HAWE jak na razie ipv6 nie dostarcza.

March 12, 2011 11:49 PM

February 22, 2011

Dopierała Andrzej

2011-02-22 IPv6 a 6to4: ipv6 6to4 sit0 routing ruch siec

Powolutku bawię się w wdrożenie IPv6 u pewnego ISP.

Na peeringach (w PIX i PLIX) ipv6 śmiga “natywnie”. Świata niestety jak na razie nikt nie dostarczył, więc leci tunelem z HE.

Do tego rozgłoszenie prefiksów do kilku testowych sieci - i troszkę ruchu widać.

W którymś momencie zauważyłem, że trochę ruchu leci do 6to4. Postawiłem więc bezpośredniego relaya(opis z http://wiki.debian.org/DebianIPv6) i…

mav-ipv6

Wniosek? Wbrew pozorom bardzo dużo (tak na oko 1/3) ruchu to ruch z relayami 6to4. Stąd zapewnienie dobrej komunikacji z nimi powinno być dosyć istotne przy udostępnianiu ipv6. Do testowego ip z którym obserwowałem ruch po ustawieniu bezpośredniego tunelowania 6to4 na moim routerze opóźnienia spadły z ok 80 do 50ms - a ruch wzrósł o kilkanaście procent.

(na wykresie jest widoczny wyłącznie ruch na tunelach - peeringi/polska leci natywnie, stąd nie mam prostej możliwości wyróżnienia go od ipv4)

February 22, 2011 11:06 PM

February 17, 2011

Dopierała Andrzej

2011-02-17 Rozkład ruchu na łączach: rozkład ruchu plix hawe tp pix

Taka mała ciekawostka.

Jest sobie mały operator. Świadczący głównie usługę dostępu do internetu dla “ludzi”. Pakiety - dosyć duże, po kilka/kilkadziesiąt megabitów, upload ciut mniejszy - po 2-3 megabity.
Ogólnie - zazwyczaj mało kto dociera do limitu w którąkolwiek ze stron.

Do tego jest troszkę hostingu i łącz biznesowych… Ale większośc to klienci indywidualni.

Łącza:
- peering w PIX-ie (http://www.pix.net.pl)
- peering w PLIX-ie (http://www.plix.pl)
- PBT HAWE (http://www.pbthawe.eu)
- Transmisję CDP Global od crowleya (http://www.crowley.pl)

i w którymś momencie na parę chwil pojawiła się testowa transmisja do TPSA - normalnie leci przez HAWE (i dalej styk z crowleyem).

Ruch - puszczony przez bgp prawie “samopas”. Lekkie preferencje by sterować ruchem na łączach zagranicznych (hawe/cdp), gdzieniegdzie unikanie problematycznych ścieżek - ale - można powiedzieć że rozkład jest prawie naturalny.

I teraz - procentowy wykres ruchu:
rozklad-lacz

Interesuje nas głównie download - uploadu jest niewiele - ok połowy downloadu, więc zazwyczaj mieści się w ramach kontraktu.

Co jest ciekawe… ok 30% ruchu zamyka się w plixie. Nawet trochę więcej.

PIX - jest malutki. poniżej 3% ruchu. Gdyby nie skala oraz fakt że jest prawie za darmo - nie było by go sensu trzymać.

HAWE i crowley - utrzymują się mniej więcej na zbliżonym poziomie - przerzucanie to głównie zmiany na ich upstreamach oraz “rzucanie” nimi (prepend przez community) by przerzucić na łącze gdzie dalej od kontraktu.

Przez jakiś czas była testowana tpsa. I tutaj niespodzianka - download był ledwo w okolicach 5% sumarycznego ruchu. Czyli - biorąc pod uwagę cenę - malutko. Z koleji upload to ok 20% ruchu - co w tej sytuacji oznaczało że było to jedyne łącze gdzie upload przekraczał download! I przypominam że to nie hosting, tylko głównie klienci indywidualni. Cóż - P2P rządzi.

February 17, 2011 11:59 AM

January 06, 2011

Dopierała Andrzej

2011-01-06 Konto w aliorbank: zakładanie konta w aliorbanku cz.1

Stwierdziłem że w mojej małej firemce przyda się kolejne konto. Obecne mam w mBanku, bo.. wiadomo - za darmo. No - prawie. 1zł za każdy przelew “na zewnątrz” - to już trochę w miesiącu wychodzi.

A Aliorbank ciągle kusi darmowym kontem, 5-cioma darmowymi przelewami w miesiącu i “premią” 50zł przy stanie konta 9k. Czyli więcej niż na lokacie w mbanku.

Z nowym rokiem - próbuję więc założyć konto.
Pierwsze wrażenia po przebrnięciu przez formularz?

  • Nie pozwala mi na wpisywanie polskich znaczków. Przeglądarka chrome. Po wklejeniu tekstu z polskimi znaczkami - ogonki znikają.
  • Do wybrania klasyfikacja usług… Ja w GUS-ie mam zgłoszoną 6202Z(działalność związana z doradztwem w zakresie informatyki - wg klasyfikacji z 2007 roku). A na stronie pozwalają na wybór pomiędzy.. 6201 a 6203.

Ale przebrnąłem - polskie znaczki jak sądzę poprawią mi w oddziale. O efektach pewnie poinformuję…

January 06, 2011 08:40 PM

October 16, 2010

Nawrot Andrzej

Rozmowy ze specjalistą ds. wdrożeń

Taki kwiatek.
Pytanie dotyczące odpowiedzialności za wykonywanie backupów pewnego systemu i odpowiedź konsultanta.

IT: Kto wykonuje backupy?

Specjalista:Backupy wykonuje MS SQL z automatu codziennie, raz na okres np. miesiąc kwartał baza powinna być nagrywana na trwały nośnik i to w zależności od firmy albo my albo dział IT
Danych nie powinniśmy stracić żadnych tylko w przypadku kataklizmu kiedy to stracimy serwer oraz kopie dyskowe – pozostaną nam tylko kopie na trwałych nośnikach, ale to sytuacje bardzo sporadyczna
Kopie na serwerze służą przede wszystkim do ewentualnych porównań w przypadku uszkodzenia – stąd codzienne kopie .

by jendras (noreply@blogger.com) at October 16, 2010 08:34 PM

June 08, 2010

Nawrot Andrzej

PowerShell via GPO

Aby zmienić ustawienia wykonywania skryptów potrzebujemy tego template'a.

http://www.microsoft.com/downloads/details.aspx?familyid=2917a564-dbbc-4da7-82c8-fe08b3ef4e6d&displaylang=en

by jendras (noreply@blogger.com) at June 08, 2010 08:29 AM

March 04, 2010

Nawrot Andrzej

Limit połączeń RDP

Ku pamięci

query session /server:servername


reset session [ID] /server:servername

by jendras (noreply@blogger.com) at March 04, 2010 02:17 PM

October 28, 2009

Nawrot Andrzej

Rodzinka w komplecie

 

Rodzinka w komplecie, brakuje tylko zniewiescialego SL :)
Posted by Picasa

by jendras (noreply@blogger.com) at October 28, 2009 10:08 PM

October 07, 2009

Najtkowski Marcin Jan

Dua Tonight

W trakcie nieustających muzycznych wojaży trafiłem znów na kawałek, którego słucham po kilkanaście-kilkadziesiąt razy dziennie. Kobiecy wokal – oczywiście, fajny, “chwytliwy” rytm oraz ogólna “sympatyczość” sprawiły, że Tonight zespołu Dua stał się moim numerem jeden ostatniego tygodnia.

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" height="265" width="320"><param name="allowFullScreen" value="true"/><param name="allowscriptaccess" value="always"/><param name="src" value="http://www.youtube-nocookie.com/v/YIfJvPm9-rg&amp;hl=pl&amp;fs=1&amp;rel=0&amp;color1=0x3a3a3a&amp;color2=0x999999"/><param name="allowfullscreen" value="true"/><embed allowfullscreen="true" allowscriptaccess="always" height="265" src="http://www.youtube-nocookie.com/v/YIfJvPm9-rg&amp;hl=pl&amp;fs=1&amp;rel=0&amp;color1=0x3a3a3a&amp;color2=0x999999" type="application/x-shockwave-flash" width="320"></embed></object>

Utwór można znaleźć na A Lounge Supreme vol. 5.

by naytec at October 07, 2009 11:40 AM