get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2223241/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2223241,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2223241/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/swupdate/patch/23a6182686a68ec88973fbc639ff0f5da73083ec.1776192954.git.konrad.schwarz@siemens.com/",
    "project": {
        "id": 58,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/58/?format=api",
        "name": "swupdate development",
        "link_name": "swupdate",
        "list_id": "swupdate.googlegroups.com",
        "list_email": "swupdate@googlegroups.com",
        "web_url": "https://github.com/sbabic/swupdate",
        "scm_url": "git://github.com/sbabic/swupdate",
        "webscm_url": ""
    },
    "msgid": "<23a6182686a68ec88973fbc639ff0f5da73083ec.1776192954.git.konrad.schwarz@siemens.com>",
    "date": "2026-04-14T18:58:48",
    "name": "[1/2] bindings/lua_swupdate: add symbolic fields to progress messages",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "39b39b381ed787f773ffcae1129dbb4f4f308043",
    "submitter": {
        "id": 92117,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/92117/?format=api",
        "name": "Konrad Schwarz",
        "email": "konrad.schwarz@gmail.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/swupdate/patch/23a6182686a68ec88973fbc639ff0f5da73083ec.1776192954.git.konrad.schwarz@siemens.com/mbox/",
    "series": [
        {
            "id": 499885,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/499885/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/swupdate/list/?series=499885",
            "date": "2026-04-14T18:58:47",
            "name": "bindings/swupdate_lua: Progress Message Ease Of Use",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/499885/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2223241/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2223241/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<swupdate+bncBCM7PUF6Q4ORBAM57LHAMGQELYU6GQY@googlegroups.com>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=googlegroups.com header.i=@googlegroups.com\n header.a=rsa-sha256 header.s=20251104 header.b=UGH3PFJs;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=fpH+BA6U;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=googlegroups.com\n (client-ip=2a00:1450:4864:20::540; helo=mail-ed1-x540.google.com;\n envelope-from=swupdate+bncbcm7puf6q4orbam57lhamgqelyu6gqy@googlegroups.com;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from mail-ed1-x540.google.com (mail-ed1-x540.google.com\n [IPv6:2a00:1450:4864:20::540])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fwD8l3p8lz1xtJ\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 15 Apr 2026 04:59:19 +1000 (AEST)",
            "by mail-ed1-x540.google.com with SMTP id\n 4fb4d7f45d1cf-6716a705e48sf1841451a12.3\n        for <incoming@patchwork.ozlabs.org>;\n Tue, 14 Apr 2026 11:59:19 -0700 (PDT)",
            "by 2002:aa7:d74e:0:b0:670:aab7:480e with SMTP id\n 4fb4d7f45d1cf-670aab75089ls2879307a12.1.-pod-prod-08-eu;\n Tue, 14 Apr 2026 11:59:11 -0700 (PDT)",
            "from mail-wr1-x42f.google.com (mail-wr1-x42f.google.com.\n [2a00:1450:4864:20::42f])\n        by gmr-mx.google.com with ESMTPS id\n 4fb4d7f45d1cf-6718452f3e2si121098a12.0.2026.04.14.11.59.11\n        for <swupdate@googlegroups.com>\n        (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n        Tue, 14 Apr 2026 11:59:11 -0700 (PDT)",
            "by mail-wr1-x42f.google.com with SMTP id\n ffacd0b85a97d-43cf7683a28so4060718f8f.2\n        for <swupdate@googlegroups.com>; Tue, 14 Apr 2026 11:59:11 -0700 (PDT)",
            "from dinmPF4XJWP1.ad001.siemens.net ([62.156.206.34])\n        by smtp.gmail.com with ESMTPSA id\n ffacd0b85a97d-43d750f0ca9sm24976942f8f.37.2026.04.14.11.59.10\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Tue, 14 Apr 2026 11:59:10 -0700 (PDT)"
        ],
        "ARC-Seal": [
            "i=2; a=rsa-sha256; t=1776193156; cv=pass;\n        d=google.com; s=arc-20240605;\n        b=RU0/yfZSIHRWh/PJBygdzy6H7m2S+ZVHMqlgxQiTXTxpe0jeebwn03LrUH049BgBEn\n         GOF4PEiZLz/ROwUSBdXNnFxTgpZGqrv3Z2JVqjmFrnuLUALsfgSoZ+LYGi8KQPTU0p6N\n         BSaVscJ+1kuKnZp1MuovhI3Yfbtno/LM+W786rYwgZTHz3vgRRsExPNaTiWYwy8ZDNi4\n         WYuZgjKJA+ADX0eeKK3O02M3gjdPjJvUH+duhiVRoilDsvdzvqh98lwY1/JjMn06z6C6\n         +vC/GSPgbbZVUQ7DPp+IvSKv1yZf/xDprBT2wLaXYD/SBvhXlpkqD60n1Oy77X1ptBIu\n         8C6w==",
            "i=1; a=rsa-sha256; t=1776193151; cv=none;\n        d=google.com; s=arc-20240605;\n        b=W/ydYbBmyGPcAmRxFGL6Ty6NeK7B437utAABZpNt6d2iLOkDRZKUEiJ7pEht6UQMJD\n         oMfax/uS3uxn9qYjxgCHtg06oWqmRKvQhyZOZcgYsKi11dzcctsODRhPxTs9xDlNCfY1\n         iATZZAQxNm/+tfoaU1QYyxxRynl8Ctq1kz0TgIn/zscaXLNcb8tSJmpLVkH0LXi5rTD9\n         zKup8SGiKW9hyK33NMM8ZD6RWlte9KxZI0r6ugdINnxmmWK2g9vV6Io3wDHmd/FMU6Y3\n         EqLRhvowZlhN3bzMi9JIyboWE2Lp4bgXv5W1qyH7zx3OVa+GDPPTHJyiaZ+w88wvt6nS\n         tugA=="
        ],
        "ARC-Message-Signature": [
            "i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n        h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post\n         :list-id:mailing-list:precedence:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:sender:dkim-signature\n         :dkim-signature;\n        bh=Cq+rLwca1+a7gwmaMSJYfMLeXA0wdSR18xa25djlzwI=;\n        fh=/Qcy6yGp0hW/0IJdnbAWtnMF7/6z5b2pdO1fbejksZ4=;\n        b=H0/vtS0MPqEfqSORlP7KLH6TAMDkcliqrmBJll5TbNIll6v6OBqd8hPwkWwJ4/zLUs\n         28RYulqUWRStkBC6JolSkq+BMfkAiHA3vZXWHdvlnj0U9hps5bXpC/Xh7F/MuUmxGUN4\n         d47yBwka/10xDNg6yfejPZmsAU5CHsx8ruzoKmSDA899Ir35WXEmPtiLM/oEM8lQOxk4\n         XWu9k2OVZGi/PpVoxckbTdRvI7Pn9yN86DnY/Raw4vI2Vf5IUk+TWAwN7GZ+tbk/7f1X\n         3+0EDXib6qIQzd6Pk3LNtKJExZghFswlmK6VWGFnb1JYg2wjjW6bVPZF8ICGf14HkvOP\n         0VuA==;\n        darn=patchwork.ozlabs.org",
            "i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com;\n s=arc-20240605;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:dkim-signature;\n        bh=2nuktRYSpzQQb0erCku2OR8fPbaHttsmJrEBGnhPFqU=;\n        fh=FthF95OvfOvalfC4EROW/dNj38UH98VcAVz7s05+GI8=;\n        b=Q5h3p1foX8nxbGMSDpLzoKST/KRzJWmFrO6703SSgDZuZ+hdZwPQNDcV91e4PmHoIm\n         WISbG35X6e0gx+MAQfZl5Z869kTLKZY1OJZOCbE1+tG8X/1APUbnzkQYiLq2LmJ3fXLr\n         blb8cnz7gxMiB/p+yJMphm317/n5cWijkNQeIZfDY170FKNTuX6yrXeEEMnH00eT5zuV\n         +/rMuDW7cXX0kNYWL6pRkA+8Rfv27Zx1Xr23nyHCQWn61KJY0Q6EeLQiL6D7BMzXUiSM\n         SiKXYLmxjh7RWA99s1Ti2mF5nryN8lgLyuqkU0o+8pVFoW06xazTwtU8eoNURdOdyIc/\n         clhg==;\n        dara=google.com"
        ],
        "ARC-Authentication-Results": [
            "i=2; gmr-mx.google.com;\n       dkim=pass header.i=@gmail.com header.s=20251104 header.b=cQno80FC;\n       spf=pass (google.com: domain of konrad.schwarz@gmail.com designates\n 2a00:1450:4864:20::42f as permitted sender)\n smtp.mailfrom=konrad.schwarz@gmail.com;\n       dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;\n       dara=pass header.i=@googlegroups.com",
            "i=1; gmr-mx.google.com;\n       dkim=pass header.i=@gmail.com header.s=20251104 header.b=cQno80FC;\n       spf=pass (google.com: domain of konrad.schwarz@gmail.com designates\n 2a00:1450:4864:20::42f as permitted sender)\n smtp.mailfrom=konrad.schwarz@gmail.com;\n       dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;\n       dara=pass header.i=@googlegroups.com"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=googlegroups.com; s=20251104; t=1776193156; x=1776797956;\n darn=patchwork.ozlabs.org;\n        h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post\n         :list-id:mailing-list:precedence:x-original-authentication-results\n         :x-original-sender:mime-version:references:in-reply-to:message-id\n         :date:subject:cc:to:from:sender:from:to:cc:subject:date:message-id\n         :reply-to;\n        bh=Cq+rLwca1+a7gwmaMSJYfMLeXA0wdSR18xa25djlzwI=;\n        b=UGH3PFJsYgJKqKRuisqFRUHLxHjKoEzlKbJKW8v9aoDf+VWpXGc+lqDe6jg8lVvhW2\n         nrYXRA8cshLtKFY7DKha8qH0POToQMays9qWsLqudGn2Zez2oc+8f/ggywvGWgV1dxwD\n         lPIVO3GFvOUklUUO/WfzVpO6GrKsBOMBsAoq8n6uvsuxirns9R65gY0QS8OGLF35rLJ5\n         88AyF+LwxN17K6W1QIwrjxrLLpx66fMlb+Q+JwHeJbNjWPKRcrnVEcBV5iay5FkqQ4WC\n         caO3w8GPOEWzZgSh7IgvUfC2thtWzdXdZcLGLmkaK1XsgHiA1ndEwo6fMdj/islH6GJh\n         r+xQ==",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1776193156; x=1776797956;\n darn=patchwork.ozlabs.org;\n        h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post\n         :list-id:mailing-list:precedence:x-original-authentication-results\n         :x-original-sender:mime-version:references:in-reply-to:message-id\n         :date:subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to;\n        bh=Cq+rLwca1+a7gwmaMSJYfMLeXA0wdSR18xa25djlzwI=;\n        b=fpH+BA6ULQXxA37AXCpErl9PxweZrdIq1vvZwUY+mU2tJnt9P/OC0fY5S2Infg1Un5\n         WRFQ6X3kHf6bP/llSpGI+fO3/jRO7lGRLZNPrNeWy+BGlLKRXoxUmporgTfuPx9Mdg70\n         PKG1vuU0F52wnpcpndPC4wESNUVZeA2jNmZDw6NIkeHxYJR2Hc4QZIhTwNK2LqWZDq1k\n         BaH+s+z3U26HgxQKo2WY6E2g/AgcCYPahHC6aI6iSJnvFupqwPOusm9xgHIrCwmc7qQI\n         v2uUCw1rvCd0nTqbe/4Gzk/uZHAi/0sSYhl/tXnc2L8/+7/okq7RIXnyBFXi0gRff4BU\n         gRmg=="
        ],
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1776193156; x=1776797956;\n        h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post\n         :x-spam-checked-in-group:list-id:mailing-list:precedence\n         :x-original-authentication-results:x-original-sender:mime-version\n         :references:in-reply-to:message-id:date:subject:cc:to:from:x-gm-gg\n         :x-beenthere:x-gm-message-state:sender:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=Cq+rLwca1+a7gwmaMSJYfMLeXA0wdSR18xa25djlzwI=;\n        b=k53+VPT8dW3KDOTaF8Fu+J08J54h2yedKKgypueLla/H+sNEQ5QdpDAxVb/09BtD/x\n         jyfL+ez1uVnXu/tIZEXXmVUATumKhGE9Dq9pw9tS5RswYS9dJ7G4S+9tPS51U1ZXu5EW\n         8EY78DDT2xIvpwN0Y6wafwFFhh8ziImxEYrwzOP8dF8ohA+/aHP1ojRi1JjvaWUHDcEy\n         M4ELUpw48KJ5zP9fw1aRGCKCUVayKkwYOx/NnjRWbIDb+gS00oT0BgC17fKyRUn25Fpo\n         Lpcx4Wp4VPCa/i/i6C1v9qWVwtXTrtXkSJmXstPviU1B3h1mzinhAmegeluBxUSivIdP\n         31WA==",
        "Sender": "swupdate@googlegroups.com",
        "X-Forwarded-Encrypted": "i=2;\n AFNElJ/HOxyIZfyE+6X+DHvlnwmI0gUMmwhUL62CDpMety7MaVr/iiQ2aqd7MpMkzUf7MsXHSCvGK9NoLQ==@patchwork.ozlabs.org",
        "X-Gm-Message-State": "AOJu0Yyjuu80RmH9265k4V3LxsApqcPtXv2OYCPMB/0x/TwDfCvKZM6L\n\tVAOOnG4OR0+xSmyzzAPt7WczHkSSXGGDuOOV4EUtJUo4ENE/r9TMen8p",
        "X-Received": [
            "by 2002:a05:6402:2750:b0:670:8b4c:3925 with SMTP id\n 4fb4d7f45d1cf-6708b4c39b1mr9757700a12.2.1776193155688;\n        Tue, 14 Apr 2026 11:59:15 -0700 (PDT)",
            "by 2002:a05:6402:1ed6:b0:671:2e65:f174 with SMTP id\n 4fb4d7f45d1cf-6712e65fc4fmr6036115a12.28.1776193151621;\n        Tue, 14 Apr 2026 11:59:11 -0700 (PDT)",
            "by 2002:a05:6000:604:b0:43d:7146:ccca with SMTP id\n ffacd0b85a97d-43d7146cd60mr16762299f8f.45.1776193150857;\n        Tue, 14 Apr 2026 11:59:10 -0700 (PDT)"
        ],
        "X-BeenThere": "swupdate@googlegroups.com;\n h=\"AYAyTiI1A86wOr5d7rd9fOs/tdcC0EIE39gBpBJ+oU4iiKv9+Q==\"",
        "Received-SPF": "pass (google.com: domain of konrad.schwarz@gmail.com designates\n 2a00:1450:4864:20::42f as permitted sender) client-ip=2a00:1450:4864:20::42f;",
        "X-Gm-Gg": "AeBDieuMdHDeFBFkU3/b41acK3G4LbhUAJcWPEDUJ2dS15x+JAPzSPKKu+CRSXQy8tp\n\tHiTldzS+wnqF5YeOBjuyYwr6T6/3fR2TxSSeo5rAefwzVrqUrc7Zn6i7li2J+wZqTldDQBNWTNP\n\tN441FXiEIsMPVaHBVOzjpZFA6dF2k5ncvmHTRrbYpsyQymuNmB7mIK0W0wxiww0ZWbvmHUFVdkp\n\tDMsgLqRGRMfdWIIjOcz4KH6X5mz+Enq8qkYHwMpz/1ORdzI2pJiTm807Fdxxndlh6x7qOCQHZT8\n\tMcXPmYreU6j3ZmhL2EQQM9/GDqDeosoA4COLHGkEqj2P+SUchRDk5E5ts/gemgicgVJCDghYiUB\n\tSpuqO1a07FW9rGU9ee+7wqNl1rCWoS8L4DUNrsuU6ZJNStVSqvkofyTS6vC4dqOrfCsrwUCG9m7\n\t1nz6lqjidfv7n3v69rENcEMc7mZ1KoJFla2pB1V8FAfUUkLljqnj878KEOZpNhmYXrEMd67Z97p\n\tQ==",
        "From": "Konrad Schwarz <konrad.schwarz@gmail.com>",
        "To": "swupdate@googlegroups.com",
        "Cc": "Konrad Schwarz <konrad.schwarz@siemens.com>",
        "Subject": "[swupdate] [PATCH 1/2] bindings/lua_swupdate: add symbolic fields to\n progress messages",
        "Date": "Tue, 14 Apr 2026 20:58:48 +0200",
        "Message-ID": "\n <23a6182686a68ec88973fbc639ff0f5da73083ec.1776192954.git.konrad.schwarz@siemens.com>",
        "X-Mailer": "git-send-email 2.47.3",
        "In-Reply-To": "<cover.1776192954.git.konrad.schwarz@siemens.com>",
        "References": "<cover.1776192954.git.konrad.schwarz@siemens.com>",
        "MIME-Version": "1.0",
        "X-Original-Sender": "konrad.schwarz@gmail.com",
        "X-Original-Authentication-Results": "gmr-mx.google.com;       dkim=pass\n header.i=@gmail.com header.s=20251104 header.b=cQno80FC;       spf=pass\n (google.com: domain of konrad.schwarz@gmail.com designates\n 2a00:1450:4864:20::42f as permitted sender)\n smtp.mailfrom=konrad.schwarz@gmail.com;\n       dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com;\n       dara=pass header.i=@googlegroups.com",
        "Content-Type": "text/plain; charset=\"UTF-8\"",
        "Precedence": "list",
        "Mailing-list": "list swupdate@googlegroups.com;\n contact swupdate+owners@googlegroups.com",
        "List-ID": "<swupdate.googlegroups.com>",
        "X-Spam-Checked-In-Group": "swupdate@googlegroups.com",
        "X-Google-Group-Id": "605343134186",
        "List-Post": "<https://groups.google.com/group/swupdate/post>,\n <mailto:swupdate@googlegroups.com>",
        "List-Help": "<https://groups.google.com/support/>,\n <mailto:swupdate+help@googlegroups.com>",
        "List-Archive": "<https://groups.google.com/group/swupdate",
        "List-Subscribe": "<https://groups.google.com/group/swupdate/subscribe>,\n <mailto:swupdate+subscribe@googlegroups.com>",
        "List-Unsubscribe": "\n <mailto:googlegroups-manage+605343134186+unsubscribe@googlegroups.com>,\n <https://groups.google.com/group/swupdate/subscribe>"
    },
    "content": "From: Konrad Schwarz <konrad.schwarz@siemens.com>\n\nProgress messages delivered via lua_swupdate.progress contain table fields\n\"status\" and \"source\", of types \"lua_swupdate.RECOVERY_STATUS\" resp.\n\"lua_swupdate.sourcetype\".\n\nSomewhat unfortunately, these fields are numeric; the tables\n\"lua_swupdate.RECOVERY_STATUS\" and \"lua_swupdate.sourcetype\" map from\nstring labels to these numeric values.  It is a bit of a chore to\ndisplay the values symbolically as human-readable strings that are\nuseful for simple monitoring and debugging: The inverse mapping of\n\"lua_swupdate.RECOVERY_STATUS\" and \"lua_swupdate.sourcetype\" must be\ngenerated and can then be indexed by the field values.\n\nAlthough these inverse mappings are not difficult to create in Lua,\nas a convenience, this patch makes the symbols (strings) available as\nthe new fields \"status_label\" and \"source_label\" in progress messages.\n\nThe patch itself takes great pains to hoist as much string\nprocessing out of the receive loop as possible: the labels of\n\"lua_swupdate.RECOVERY_STATUS\" and \"lua_swupdate.sourcetype\", as well\nas the field names are recorded in Lua exactly once, in the package's\nluaopen_ routine.  To prevent the strings from being garbage collected,\nthey (or tables holding them) are registered as light userdata in the\nLua registry.  No so such string is registered twice; the intent is that\nstring comparision can be done quickly as pointer identity checks.\n\nNote that the (undocumented) internment of short strings apparently\nimplemented in at least some Lua versions provides most of the same\nbenefits -- except that an extra hash table lookup is required to check\nif a short string has been interned already each time such a string is\npassed to Lua.\tThe purely functional improvement could be implemented\nin simpler fashion.\n\nSigned-off-by: Konrad Schwarz <konrad.schwarz@siemens.com>\n---\n bindings/lua_swupdate.c   | 418 ++++++++++++++++++++++++++++++++------\n bindings/lua_swupdate.lua |  10 +-\n 2 files changed, 368 insertions(+), 60 deletions(-)",
    "diff": "diff --git a/bindings/lua_swupdate.c b/bindings/lua_swupdate.c\nindex bd5f876a..e81f4030 100644\n--- a/bindings/lua_swupdate.c\n+++ b/bindings/lua_swupdate.c\n@@ -2,15 +2,15 @@\n  * (C) Copyright 2018\n  * Stefano Babic, stefano.babic@swupdate.org.\n  *\n- * SPDX-License-Identifier:     LGPL-2.1-or-later\n+ * SPDX-License-Identifier:\tLGPL-2.1-or-later\n  */\n \n #include <progress_ipc.h>\n #include <lua.h>\n #include <lauxlib.h>\n-#include <string.h> \n+#include <string.h>\n #include <ifaddrs.h>\n-#include <netinet/in.h> \n+#include <netinet/in.h>\n #include <net/if.h>\n #include <arpa/inet.h>\n #include <unistd.h>\n@@ -25,27 +25,66 @@\n #include \"auxiliar.h\"\n #include \"lua_compat.h\"\n \n+/* Comment Legend:\n+ * Lua uses an argument stack for object manipulation.\n+ * Routines that manipulate the stack in non-trival ways\n+ * use the following notation to indicate the current stack layout,\n+ * derived from FORTH stack notation:\n+ */\n+    /* stack_bottom ... stack_top */\n+/*\n+ * I.e., the rightmost element is the stack top.\n+ * A stack entry is identified\n+ * with a code that is supposed to be mnemonic,\n+ * typically utilizing an abbreviation of its type.\n+ * E.g., \"t_a\" for \"table A\".\n+ *\n+ * For referencce: LUa defines stack offsets as follows:\n+ *\n+ * 0 1 2 \t ...     -2 -1\n+ *\n+ */\n+\n+\n+/* always returning the type helps assertion checking */\n+# if 503 > LUA_VERSION_NUM\n+#\tdefine\tPR_LUA_RAWGET(L, index)\\\n+\t\t(lua_rawget (L, index), lua_type (L, -1))\n+#\tdefine\tPR_LUA_RAWGETI(L, index, integer)\\\n+\t\t(lua_rawgeti (L, index, integer), lua_type (L, -1))\n+# else\n+#\tdefine\tPR_LUA_RAWGET(L, index)\\\n+\t\tlua_rawget (L, index)\n+#\tdefine\tPR_LUA_RAWGETI(L, index, integer)\\\n+\t\tlua_rawgeti (L, index, integer)\n+# endif\n+\n+\n+/* work around the non-const-qualification of argument p */\n+# define\tPR_LUA_PUSHLIGHTUSERDATA(L, P)\\\n+\tlua_pushlightuserdata (L, (void *) P)\n+\n #define WAIT 1\n \n-#define LUA_PUSH_STRING(key, data)  do { \t\\\n+#define LUA_PUSH_STRING(key, data)  do {\t\\\n \tlua_pushstring(L, key);\t\t\t\\\n \tlua_pushstring(L, data);\t\t\\\n \tlua_settable(L, -3);\t\t\t\\\n } while (0)\n \n-#define LUA_PUSH_BOOL(key, data)  do { \t\t\\\n+#define LUA_PUSH_BOOL(key, data)  do {\t\t\\\n \tlua_pushstring(L, key);\t\t\t\\\n \tlua_pushboolean(L, data);\t\t\\\n \tlua_settable(L, -3);\t\t\t\\\n } while (0)\n \n-#define LUA_PUSH_NUMBER(key, data)  do { \t\\\n+#define LUA_PUSH_NUMBER(key, data)  do {\t\\\n \tlua_pushstring(L, key);\t\t\t\\\n \tlua_pushnumber(L, (double) data);\t\\\n \tlua_settable(L, -3);\t\t\t\\\n } while (0)\n \n-#define LUA_PUSH_INT(key, data)  do { \t\t\\\n+#define LUA_PUSH_INT(key, data)  do {\t\t\\\n \tlua_pushstring(L, key);\t\t\t\\\n \tlua_pushinteger(L, data);\t\t\\\n \tlua_settable(L, -3);\t\t\t\\\n@@ -62,8 +101,6 @@ static int ctrl_write(lua_State *L);\n static int ctrl_close(lua_State *L);\n static int ctrl_close_socket(lua_State *L);\n \n-int luaopen_lua_swupdate(lua_State *L);\n-\n /*\n  * Return a table with all interface and their IP address\n  */\n@@ -118,7 +155,7 @@ static luaL_Reg ctrl_methods[] = {\n \t{\"connect\",    ctrl_connect},\n \t{\"write\",      ctrl_write},\n \t{\"close\",      ctrl_close},\n-\t{NULL,         NULL}\n+\t{}\n };\n \n /**\n@@ -271,12 +308,12 @@ struct prog_obj {\n \n /* progress object methods */\n static luaL_Reg progress_methods[] = {\n-    {\"__gc\",        progress_close},\n-    {\"__tostring\",  auxiliar_tostring},\n-    {\"close\",       progress_close},\n-    {\"connect\",     progress_connect},\n-    {\"receive\",     progress_receive},\n-    {NULL,          NULL}\n+    {\"__gc\",\t\tprogress_close},\n+    {\"__tostring\",\tauxiliar_tostring},\n+    {\"close\",\t\tprogress_close},\n+    {\"connect\",\t\tprogress_connect},\n+    {\"receive\",\t\tprogress_receive},\n+    {}\n };\n \n /**\n@@ -284,7 +321,7 @@ static luaL_Reg progress_methods[] = {\n  *\n  * @param  [Lua] The swupdate_progress class instance.\n    @return [Lua] The connection handle (mostly for information), or,\n- *               in case of errors, nil plus an error message.\n+ *\t\t in case of errors, nil plus an error message.\n  */\n static int progress_connect(lua_State *L) {\n \tstruct prog_obj *p = (struct prog_obj *) auxiliar_checkclass(L, \"swupdate_progress\", 1);\n@@ -301,37 +338,207 @@ static int progress_connect(lua_State *L) {\n \tp->socket = connfd;\n \tp->status = IDLE;\n \n-\tlua_pop(L, 1);\n \tlua_pushnumber(L, connfd);\n-\tlua_pushnil(L);\n \n-\treturn 2;\n+\treturn 1;\n }\n \n static int progress_close(lua_State __attribute__ ((__unused__)) *L) {\n \treturn 1;\n }\n \n-static int progress_receive(lua_State *L) {\n+\n+# define ALL_PROGRESS_LABELS(MACRO)\\\n+\tMACRO (apiversion)\\\n+\tMACRO (status)\\\n+\tMACRO (download)\\\n+\tMACRO (source)\\\n+\tMACRO (nsteps)\\\n+\tMACRO (step)\\\n+\tMACRO (percent)\\\n+\tMACRO (artifact)\\\n+\tMACRO (handler)\\\n+\tMACRO (info)\\\n+\tMACRO (status_label)\\\n+\tMACRO (source_label)\\\n+\n+static struct progress_field {\n+\tchar const *name;\n+\tsize_t len;\n+} const progress_field [] = {\n+\n+# define PROGRESS_FIELD_STRING(LABEL)\\\n+\t{ #LABEL, sizeof #LABEL - sizeof \"\" },\n+\n+\tALL_PROGRESS_LABELS (PROGRESS_FIELD_STRING)\n+\n+# undef PROGRESS_FIELD_STRING\n+};\n+\n+enum progress_field_enum {\n+\n+# define PROGRESS_FIELD_ENUM(LABEL)\\\n+\tprogress_field_ ## LABEL,\n+\n+\tALL_PROGRESS_LABELS (PROGRESS_FIELD_ENUM)\n+\n+# undef PROGRESS_FIELD_ENUM\n+\n+\tprogress_field_enum_last\n+\n+};\n+\n+/**\n+ * @brief Retrieve the label of a mesasge field and place it at top of stack\n+ *\n+ * @param<in>  field_key\tthe message field's light user data\n+ */\n+static void progress_get_label (lua_State *const L,\n+\t\tstruct progress_field const *const field_key)\n+{\n+\tint type;\n+\n+\t/* */\n+\tPR_LUA_PUSHLIGHTUSERDATA (L, field_key);\n+\t/* luf */\n+\tif (LUA_TSTRING != (type = PR_LUA_RAWGET (L, LUA_REGISTRYINDEX)))\n+\t\tluaL_error (L, \"Expecting a string, got a %s\",\n+\t\t\t\tlua_typename (L, type));\n+\t/* fs */\n+}\n+\n+static void progress_set_integer (lua_State *const L,\n+\t\tstruct progress_field const *const field_key,\n+\t\tlua_Integer const value)\n+{\n+\t/* pt */\n+\tprogress_get_label (L, field_key);\n+\t/* pt fs */\n+\tlua_pushinteger (L, value);\n+\t/* pt fs i */\n+\tlua_rawset (L, -3);\n+\t/* pt */\n+}\n+\n+static void progress_set_string (lua_State *const L,\n+\t\tstruct progress_field const *const field_key,\n+\t\tchar const *const value)\n+{\n+\t/* pt */\n+\tprogress_get_label (L, field_key);\n+\t/* pt fs */\n+\tlua_pushstring (L, value);\n+\t/* pt fs s */\n+\tlua_rawset (L, -3);\n+\t/* pt */\n+}\n+\n+static void progress_set_lstring (lua_State *const L,\n+\t\tstruct progress_field const *const field_key,\n+\t\tchar const *const value,\n+\t\tsize_t const len)\n+{\n+\t/* pt */\n+\tprogress_get_label (L, field_key);\n+\t/* pt fs */\n+\tlua_pushlstring (L, value, len);\n+\t/* pt fs s */\n+\tlua_rawset (L, -3);\n+\t/* pt */\n+}\n+\n+static void progress_set_label (lua_State *const L,\n+\t\tstruct progress_field const *const field_key,\n+\t\tint const *const lu_anchor,\n+\t\tlua_Integer const label)\n+{\n+\tint type;\n+\t/* pt */\n+\tprogress_get_label (L, field_key);\n+\t/* pt fs */\n+\tPR_LUA_PUSHLIGHTUSERDATA (L, lu_anchor);\n+\t/* pt fs lu */\n+\tif (LUA_TTABLE != (type = PR_LUA_RAWGET (L, LUA_REGISTRYINDEX)))\n+\t\tluaL_error (L, \"Expecting a table, got a %s\",\n+\t\t\t\tlua_typename (L, type));\n+\t/* pt fs t */\n+\tif (LUA_TSTRING != (type = PR_LUA_RAWGETI (L, -1, label)))\n+\t\tluaL_error (L, \"Expecting a string, got a %s\",\n+\t\t\t\tlua_typename (L, type));\n+\t/* pt fs t s */\n+\tlua_remove (L, -2);\n+\t/* pt fs s */\n+\tlua_rawset (L, -3);\n+\t/* pt */\n+}\n+\n+/* light user data anchors -- otherwise unused.\n+ * note that (the address of) string constants cannot be used to anchor\n+ * light user data, as the linker is allowed to overlay these constants with\n+ * others to optimize space */\n+static int const recovery_table, source_type_table;\n+\n+static int progress_receive(lua_State *const L)\n+{\n \tstruct prog_obj *p = (struct prog_obj *) auxiliar_checkclass(L, \"swupdate_progress\", 1);\n \tint connfd = p->socket;\n \tif (progress_ipc_receive(&connfd, &p->msg) <= 0) {\n-        \tlua_pushnil(L);\n-\t\treturn 2;\n+\t\tlua_pushnil(L);\n+\t\treturn 1;\n \t};\n \tlua_newtable(L);\n \n-\tLUA_PUSH_INT(\"apiversion\", p->msg.apiversion);\n-\tLUA_PUSH_INT(\"status\", p->msg.status);\n-\tLUA_PUSH_INT(\"download\", p->msg.dwl_percent);\n-\tLUA_PUSH_INT(\"source\", p->msg.source);\n-\tLUA_PUSH_INT(\"nsteps\", p->msg.nsteps);\n-\tLUA_PUSH_INT(\"step\", p->msg.cur_step);\n-\tLUA_PUSH_INT(\"percent\", p->msg.cur_percent);\n-\tLUA_PUSH_STRING(\"artifact\", p->msg.cur_image);\n-\tLUA_PUSH_STRING(\"handler\", p->msg.hnd_name);\n+\t/* pt */\n+\n+# define\tPROGRESS_SET_INTEGER(FIELD, MSG_FIELD)\\\n+\tdo {\\\n+\t\tprogress_set_integer (L,\\\n+\t\t\tprogress_field + progress_field_ ## FIELD,\\\n+\t\t\tp->msg.MSG_FIELD);\\\n+\t} while (0)\n+\n+# define\tPROGRESS_SET_STRING(FIELD, MSG_FIELD)\\\n+\tdo {\\\n+\t\tprogress_set_string (L,\\\n+\t\t\tprogress_field + progress_field_ ## FIELD,\\\n+\t\t\tp->msg.MSG_FIELD);\\\n+\t} while (0)\n+\n+# define\tPROGRESS_SET_LSTRING(FIELD, MSG_FIELD, LEN)\\\n+\tdo {\\\n+\t\tprogress_set_lstring (L,\\\n+\t\t\tprogress_field + progress_field_ ## FIELD,\\\n+\t\t\tp->msg.MSG_FIELD, LEN);\\\n+\t} while (0)\n+\n+# define\tPROGRESS_SET_LABEL(FIELD, LABEL_TABLE, MSG_FIELD)\\\n+\tdo {\\\n+\t\tprogress_set_label (L,\\\n+\t\t\tprogress_field + progress_field_ ## FIELD ## _label,\\\n+\t\t\tLABEL_TABLE,\\\n+\t\t\tp->msg.MSG_FIELD);\\\n+\t} while (0)\n+\n+\tPROGRESS_SET_INTEGER (apiversion, apiversion);\n+\tPROGRESS_SET_INTEGER (status, status);\n+\tPROGRESS_SET_INTEGER (download, dwl_percent);\n+\tPROGRESS_SET_INTEGER (source, source);\n+\tPROGRESS_SET_INTEGER (nsteps, nsteps);\n+\tPROGRESS_SET_INTEGER (step, cur_step);\n+\tPROGRESS_SET_INTEGER (percent, cur_percent);\n+\tPROGRESS_SET_STRING (artifact, cur_image);\n+\tPROGRESS_SET_STRING (handler, hnd_name);\n \tif (p->msg.infolen)\n-\t\tLUA_PUSH_STRING(\"info\", p->msg.info);\n+\t\tPROGRESS_SET_LSTRING (info, info, p->msg.infolen);\n+\n+\tPROGRESS_SET_LABEL (status, &recovery_table, status);\n+\tPROGRESS_SET_LABEL (source, &source_type_table, source);\n+\n+# undef\tPROGRESS_SET_LABEL\n+# undef\tPROGRESS_SET_STRING\n+# undef\tPROGRESS_SET_INTEGER\n+\n+\t/* pt */\n \n \tp->status = p->msg.status;\n \n@@ -354,39 +561,136 @@ static const luaL_Reg lua_swupdate[] = {\n   {\"progress\", progress},\n   {\"control\", ctrl},\n   {\"ipv4\", netif},\n-  {NULL, NULL}\n+  {}\n };\n \n /*\n  * Initialization of C module\n  */\n-int luaopen_lua_swupdate(lua_State *L){\n-\tluaL_newlib(L, lua_swupdate);\n \n-\t/* Export the RECOVERY_STATUS enum */\n-\tlua_pushstring(L, \"RECOVERY_STATUS\");\n+struct label_init {\n+\tint cardinal;\n+\tchar const *label;\n+\tsize_t label_len;\n+};\n+\n+/**\n+ * @brief Initialize label mapping table and its inverse.\n+ *\n+ * The lua module table must be at the top of the Lua stack.\n+ * On return, the stack has the same form.\n+ *\n+ * @param<in>  table_name: the table's name\n+ * @param<in>  table_name_len: the table name's length\n+ * @param<in>  anchor: a unique address used to anchor the inverse table as Lua lightuserdata\n+ * @param<in>  li: the start of the list of labels to be stored in the table and its inverse\n+ * @param<in>  li_end: the end of the list of labels to be stored in the table and its inverse\n+ */\n+static void process_labels (lua_State *const L,\n+\t\tchar const *const table_name,\n+\t\tsize_t const table_name_len,\n+\t\tint const *const anchor,\n+\t\tstruct label_init const *li,\n+\t\tstruct label_init const *const li_end)\n+{\n+\t/* mt */\n+# if\t0\n+\tchar const *const table_lua =\n+# endif\n+\tlua_pushlstring (L, table_name, table_name_len);\n+\t/* mt tn */\n \tlua_newtable (L);\n-\tLUA_PUSH_INT(\"IDLE\", IDLE);\n-\tLUA_PUSH_INT(\"START\", START);\n-\tLUA_PUSH_INT(\"RUN\", RUN);\n-\tLUA_PUSH_INT(\"SUCCESS\", SUCCESS);\n-\tLUA_PUSH_INT(\"FAILURE\", FAILURE);\n-\tLUA_PUSH_INT(\"DOWNLOAD\", DOWNLOAD);\n-\tLUA_PUSH_INT(\"DONE\", DONE);\n-\tLUA_PUSH_INT(\"SUBPROCESS\", SUBPROCESS);\n-\tLUA_PUSH_INT(\"PROGRESS\", PROGRESS);\n-\tlua_settable(L, -3);\n+\t/* mt tn t */\n+\tlua_newtable (L);\n+\t/* mt tn t t_inv */\n+\tPR_LUA_PUSHLIGHTUSERDATA (L, anchor);\n+\t/* mt tn t t_inv lu_a */\n+\tlua_pushvalue (L, -2);\n+\t/* mt tn t t_inv lu_a_inv t_inv */\n+\tlua_rawset (L, LUA_REGISTRYINDEX);\n+\t/* mt tn t t_inv */\n+\n+\tfor (; li_end > li; ++li) {\n+\t\t/* mt tn t t_inv */\n+# if\t0\n+\t\tchar const *const label_lua =\n+# endif\n+\t\tlua_pushlstring (L, li->label, li->label_len);\n+\t\t/* mt tn t t_inv s */\n+\t\tlua_pushinteger (L, li->cardinal);\n+\t\t/* mt tn t t_inv s i */\n+\t\tlua_pushvalue (L, -2);\n+\t\t/* mt tn t t_inv s i s */\n+\t\tlua_rawseti (L, -4, li->cardinal);\n+\t\t/* mt tn t t_inv s i */\n+\t\tlua_rawset (L, -4);\n+\t\t/* mt tn t t_inv */\n+\t}\n+\t/* mt tn t t_inv */\n+\tlua_pop (L, 1);\n+\t/* mt tn t */\n+\tlua_rawset (L, -3);\n+\t/* mt */\n+}\n+\n+# pragma GCC diagnostic ignored \"-Wmissing-prototypes\"\n+int\n+luaopen_lua_swupdate(lua_State *L)\n+{\n+\tluaL_newlib(L, lua_swupdate);\n+\t/* mt */\n+\n+# define LABEL_INIT(X)\\\n+\t{ X, #X, sizeof #X - sizeof \"\" },\n \n+\t/* Export the RECOVERY_STATUS enum */\n+\tstatic struct label_init const recovery_labels [] = {\n+\t\tLABEL_INIT (IDLE)\n+\t\tLABEL_INIT (START)\n+\t\tLABEL_INIT (RUN)\n+\t\tLABEL_INIT (SUCCESS)\n+\t\tLABEL_INIT (FAILURE)\n+\t\tLABEL_INIT (DOWNLOAD)\n+\t\tLABEL_INIT (DONE)\n+\t\tLABEL_INIT (SUBPROCESS)\n+\t\tLABEL_INIT (PROGRESS)\n+\t};\n+\tstatic char const recovery_status [] = \"RECOVERY_STATUS\";\n+\tprocess_labels (L, recovery_status, sizeof recovery_status - sizeof \"\",\n+\t\t\t&recovery_table,\n+\t\t\trecovery_labels, 1 [&recovery_labels]);\n+\t/* mt */\n \t/* Export the sourcetype enum */\n-\tlua_pushstring(L, \"sourcetype\");\n-\tlua_newtable (L);\n-\tLUA_PUSH_INT(\"SOURCE_UNKNOWN\", SOURCE_UNKNOWN);\n-\tLUA_PUSH_INT(\"SOURCE_WEBSERVER\", SOURCE_WEBSERVER);\n-\tLUA_PUSH_INT(\"SOURCE_SURICATTA\", SOURCE_SURICATTA);\n-\tLUA_PUSH_INT(\"SOURCE_DOWNLOADER\", SOURCE_DOWNLOADER);\n-\tLUA_PUSH_INT(\"SOURCE_LOCAL\", SOURCE_LOCAL);\n-\tLUA_PUSH_INT(\"SOURCE_CHUNKS_DOWNLOADER\", SOURCE_CHUNKS_DOWNLOADER);\n-\tlua_settable(L, -3);\n+\tstatic struct label_init const source_labels [] = {\n+\n+# define\tSOURCE_LABEL_INIT(S)\\\n+\t\tLABEL_INIT (SOURCE_ ## S)\n+\n+\t\tSOURCE_LABEL_INIT (UNKNOWN)\n+\t\tSOURCE_LABEL_INIT (WEBSERVER)\n+\t\tSOURCE_LABEL_INIT (SURICATTA)\n+\t\tSOURCE_LABEL_INIT (DOWNLOADER)\n+\t\tSOURCE_LABEL_INIT (LOCAL)\n+\t\tSOURCE_LABEL_INIT (CHUNKS_DOWNLOADER)\n+\t};\n+\tstatic char const source_type [] = \"sourcetype\";\n+\tprocess_labels (L, source_type, sizeof source_type - sizeof \"\",\n+\t\t\t&source_type_table,\n+\t\t\tsource_labels, 1 [&source_labels]);\n+\t/* mt */\n+\n+\tstruct progress_field const *f;\n+\tfor (f = progress_field; 1 [&progress_field] > f; ++f) {\n+\t\t/* mt */\n+\t\tPR_LUA_PUSHLIGHTUSERDATA (L, f);\n+\t\t/* mt luf */\n+\t\tlua_pushlstring (L, f->name, f->len);\n+\t\t/* mt luf s */\n+\t\tlua_rawset (L, LUA_REGISTRYINDEX);\n+\t\t/* mt */\n+\t}\n+\n+\t/* mt */\n \n \tauxiliar_newclass(L, \"swupdate_progress\", progress_methods);\n \tauxiliar_newclass(L, \"swupdate_control\", ctrl_methods);\ndiff --git a/bindings/lua_swupdate.lua b/bindings/lua_swupdate.lua\nindex b21294b5..f3470189 100644\n--- a/bindings/lua_swupdate.lua\n+++ b/bindings/lua_swupdate.lua\n@@ -43,7 +43,7 @@ lua_swupdate.RECOVERY_STATUS = {\n     DOWNLOAD   = 5,\n     DONE       = 6,\n     SUBPROCESS = 7,\n-    PROGRESS   = 8\n+    PROGRESS   = 8,\n }\n \n \n@@ -55,7 +55,7 @@ lua_swupdate.sourcetype = {\n     SOURCE_SURICATTA         = 2,\n     SOURCE_DOWNLOADER        = 3,\n     SOURCE_LOCAL             = 4,\n-    SOURCE_CHUNKS_DOWNLOADER = 5\n+    SOURCE_CHUNKS_DOWNLOADER = 5,\n }\n \n \n@@ -70,7 +70,11 @@ lua_swupdate.sourcetype = {\n --- @field percent    number  Percentage done in current step\n --- @field artifact   string  Name of image to be installed\n --- @field handler    string  Name of running Handler\n---- @field info       string  Additional information about installation\n+--- @field info       string  Additional information about installation, optional\n+---\n+--- @field status_label string\tThe (string) label corresponding to field status\n+--- @field source_label string\tThe (string) label corresponding to field source\n+\n \n \n --- SWUpdate progress socket binding.\n",
    "prefixes": [
        "1/2"
    ]
}