+++ /dev/null
-/**
- @file
- @ingroup anvil
-*/
-#include <stdc.h>
-#include <utf.h>
-#include <edit.h>
-#include <win.h>
-#include <x11.h>
-#include <draw.h>
-#include <locale.h>
-#include <sys/wait.h>
-#include <X11/cursorfont.h>
-
-#define INCLUDE_DEFS
-#include "config.h"
-
-#define BARHEIGHT(x) \
- ((x)->font->height + 3)
-
-#define TCLIENT(c) \
- container_of(c, Client, tnode)
-
-#define MCLIENT(c) \
- container_of(c, Client, mnode)
-
-#define Tiled_Clients \
- (Desktops[Desktop].tiled)
-
-#define Floated_Clients \
- (Desktops[Desktop].floated)
-
-enum {
- TOO_SMALL = (1 << 0),
- FLOATING = (1 << 1),
-};
-
-typedef struct Node {
- struct Node* next;
- struct Node* prev;
-} Node;
-
-typedef void* (*Reducer)(Node* node, void* accum);
-
-typedef struct Client {
- Node mnode, tnode;
- Window win;
- Window frame;
- char* name;
- XftDraw* xft;
- int flags, x, y, w, h;
-} Client;
-
-typedef struct {
- Client* c;
- size_t nclients, nexty, sh;
-} GrowState;
-
-typedef struct Column {
- struct Column* next;
- struct Client* clients;
- int flags, x, w;
-} Column;
-
-static struct {
- Node *floated, *tiled;
-} Desktops[10] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
-static int Desktop = 1;
-XConf X = {0};
-static Node* All_Clients = NULL;
-static Cursor Move_Cursor;
-static Cursor Main_Cursor;
-static int StartY = 0;
-
-/* configuration */
-static uint32_t BorderWidth = 1;
-static uint32_t BorderColor = 0xFF0000;
-static uint32_t BackgroundColor = 0x000077;
-
-static Atom
- XA_NET_WM_WINDOW_TYPE,
- XA_NET_WM_WINDOW_TYPE_DIALOG,
- XA_WM_PROTOCOLS;
-
-typedef union {
- const char** com;
- const int i;
-} Arg;
-
-struct key {
- unsigned int mod;
- KeySym keysym;
- void (*function)(const Arg arg);
- const Arg arg;
-};
-
-static void change_desktop(const Arg arg);
-static void client_to_desktop(const Arg arg);
-
-#define MOD Mod1Mask
-#define DESKTOPCHANGE(K,N) \
- { MOD, K, change_desktop, {.i = N}}, \
- { MOD|ShiftMask, K, client_to_desktop, {.i = N}},
-
-static struct key keys[] = {
- // MOD KEY FUNCTION ARGS
-// { MOD, XK_c, spawn, {.com = lockcmd}},
-// { MOD, XK_p, spawn, {.com = dmenucmd}},
-// { MOD|ShiftMask, XK_Return, spawn, {.com = urxvtcmd}},
-// { MOD|ShiftMask, XK_q, quit, {NULL}}
- DESKTOPCHANGE( XK_0, 0)
- DESKTOPCHANGE( XK_1, 1)
- DESKTOPCHANGE( XK_2, 2)
- DESKTOPCHANGE( XK_3, 3)
- DESKTOPCHANGE( XK_4, 4)
- DESKTOPCHANGE( XK_5, 5)
- DESKTOPCHANGE( XK_6, 6)
- DESKTOPCHANGE( XK_7, 7)
- DESKTOPCHANGE( XK_8, 8)
- DESKTOPCHANGE( XK_9, 9)
-};
-
-/* Utility Functions
- *****************************************************************************/
-static void die(char* errstr)
-{
- fprintf(stderr, "error: %s\n", errstr);
- exit(1);
-}
-
-static Atom atom(XConf* x, char* s)
-{
- return XInternAtom(x->display, s, False);
-}
-
-static void* xfree(void* p)
-{
- if (p)
- {
- XFree(p);
- }
- return NULL;
-}
-
-static int get_prop(XConf* x, Window w, Atom name, Atom* type, int* format, void** data, size_t* ndata)
-{
- unsigned long nleft;
- return XGetWindowProperty(
- x->display, w, name, 0, -1, False, AnyPropertyType, type, format, ndata, &nleft, (unsigned char**)data);
-}
-
-/* List Handling
- *****************************************************************************/
-static void list_add(Node** list, Node* parent, Node* node)
-{
- if (!parent)
- {
- node->next = *list;
- node->prev = NULL;
- if (*list) (*list)->prev = node;
- *list = node;
- }
- else
- {
- node->next = parent->next;
- node->prev = parent;
- parent->next = node;
- if (node->next)
- {
- node->next->prev = node;
- }
- }
-}
-
-static void list_del(Node** list, Node* node)
-{
- if (node->prev)
- {
- node->prev->next = node->next;
- }
- if (node->next)
- {
- node->next->prev = node->prev;
- }
- node->next = NULL, node->prev = NULL;
- if (*list == node)
- {
- *list = node->next;
- }
-}
-
-static void* list_reduce(Node* list, Reducer rfn, void* accum)
-{
- return (list ? list_reduce(list->next, rfn, rfn(list, accum)) : accum);
-}
-
-static size_t list_count(Node* curr)
-{
- size_t nclients = 0;
- for (; curr; curr = curr->next, nclients++);
- return nclients;
-}
-
-/* Client Handling
- *****************************************************************************/
-static void client_reconfig(XConf* xs, Client* c);
-static void client_config(XConf* xs, Client* c, int x, int y, int w, int h);
-
-static void* biggest(Node* node, void* accum)
-{
- if (!accum || TCLIENT(node)->h > ((Client*)accum)->h)
- {
- accum = TCLIENT(node);
- }
- return accum;
-}
-
-static void* find_win(Node* n, void* env)
-{
- struct { Window w; Client* c; }* state = env;
- if ((MCLIENT(n)->frame == state->w) || (MCLIENT(n)->win == state->w))
- {
- state->c = MCLIENT(n);
- }
- return env;
-}
-
-static void* grow_client(Node* node, void* env)
-{
- GrowState* state = env;
- Client* curr = TCLIENT(node);
- state->nclients--;
- curr->y = state->nexty;
- if (curr == state->c)
- {
- curr->h = state->sh - state->nexty - (state->nclients * BARHEIGHT(&X));
- }
- else
- {
- curr->h = BARHEIGHT(&X);
- }
- client_reconfig(&X, curr);
- state->nexty += curr->h;
- return state;
-}
-
-static int client_flags(Client* c, int mask)
-{
- return (c->flags & mask);
-}
-
-static void client_raise(XConf* x, Client* c)
-{
- XMapWindow(x->display, c->frame);
- XMapWindow(x->display, c->win);
- if (!client_flags(c, FLOATING))
- {
- XLowerWindow(x->display, c->frame);
- XLowerWindow(x->display, c->win);
- }
- else
- {
- XRaiseWindow(x->display, c->frame);
- XRaiseWindow(x->display, c->win);
- }
- XSync(x->display, False);
-}
-
-static void client_add(Client* c, int dtop)
-{
- list_add(&All_Clients, NULL, &(c->mnode));
- if (!client_flags(c, FLOATING))
- {
- Client* parent = list_reduce(Desktops[dtop].tiled, biggest, NULL);
- list_add(&Desktops[dtop].tiled, (parent ? &(parent->tnode) : NULL), &(c->tnode));
- if (parent)
- {
- c->h = (parent->h - (parent->h / 2));
- parent->h /= 2;
- c->y = parent->y + parent->h;
- client_config(&X, parent, parent->x, parent->y, parent->w, parent->h);
- }
- }
- else
- {
- list_add(&Desktops[dtop].floated, NULL, &(c->tnode));
- }
-}
-
-static void client_del(Client* c)
-{
- list_del(&All_Clients, &(c->mnode));
- if (!client_flags(c, FLOATING))
- {
- list_del(&Tiled_Clients, &(c->tnode));
- int y = c->y, h = c->h;
- Node* nsucc = (c->tnode.prev ? c->tnode.prev : c->tnode.next);
- if (nsucc)
- {
- Client* succ = TCLIENT(nsucc);
- succ->h += h;
- if (nsucc == c->tnode.next)
- succ->y = y;
- client_config(&X, succ, succ->x, succ->y, succ->w, succ->h);
- client_raise(&X, succ);
- }
- }
- else
- {
- list_del(&Floated_Clients, &(c->tnode));
- }
-}
-
-static void client_redraw(XConf* x, Client* c)
-{
- XftColor fgclr, bgclr;
- if (!c->name) return;
- xftcolor(x, &fgclr, -1);
- xftcolor(x, &bgclr, BackgroundColor);
- XftDrawRect(c->xft, &bgclr, 0, 0, c->w, BARHEIGHT(x));
- XftDrawStringUtf8(c->xft, &fgclr, x->font, 0, x->font->ascent, (const FcChar8*)c->name, strlen(c->name));
- XftColorFree(x->display, x->visual, x->colormap, &fgclr);
- XftColorFree(x->display, x->visual, x->colormap, &bgclr);
-}
-
-static void client_reconfig(XConf* xs, Client* c)
-{
- int height = BARHEIGHT(xs);
- XMoveResizeWindow(xs->display, c->frame, c->x, c->y, c->w - 2, height);
- if (c->h <= height)
- {
- XUnmapWindow(xs->display, c->win);
- c->flags |= TOO_SMALL;
- }
- else
- {
- XMoveResizeWindow(xs->display, c->win, c->x, c->y + height, c->w - 2, c->h - height - 2);
- if (client_flags(c, TOO_SMALL))
- {
- c->flags &= ~TOO_SMALL;
- XMapWindow(xs->display, c->win);
- }
- }
- client_redraw(xs, c);
- XSync(xs->display, False);
-}
-
-static void client_config(XConf* xs, Client* c, int x, int y, int w, int h)
-{
- c->x = x, c->y = y, c->w = w, c->h = h;
- client_reconfig(xs, c);
- client_redraw(xs, c);
-}
-
-static void client_initprops(XConf* x, Client* c)
-{
- int nprops = 0;
- Atom* props = XListProperties(x->display, c->win, &nprops);
- Atom type;
- int format;
- void* data = 0;
- size_t ndata;
- for (int i = 0; i < nprops; i++)
- {
- if (props[i] == XA_NET_WM_WINDOW_TYPE)
- {
- if (Success == get_prop(x, c->win, props[i], &type, &format, &data, &ndata))
- {
- if (((Atom*)data)[0] == XA_NET_WM_WINDOW_TYPE_DIALOG)
- {
- c->flags |= FLOATING;
- }
- }
- }
- else if (props[i] == XA_WM_PROTOCOLS)
- {
- /* register desire for WM_DELETE message here */
- }
- data = xfree(data);
- }
-}
-
-static void client_create(XConf* x, Window win)
-{
- Client* c = calloc(1, sizeof(Client));
- c->win = win;
- XGrabServer(x->display);
- XFetchName(x->display, win, &c->name);
- client_initprops(x, c);
-
- /* create the frame window */
- if (client_flags(c, FLOATING))
- {
- XWindowAttributes attr;
- XGetWindowAttributes(x->display, c->win, &attr);
- c->x = attr.x, c->y = attr.y;
- c->w = attr.width;
- c->h = attr.height;
- }
- else
- {
- c->x = 0, c->y = 0;
- c->w = WidthOfScreen(DefaultScreenOfDisplay(x->display));
- c->h = HeightOfScreen(DefaultScreenOfDisplay(x->display));
- }
- c->frame = XCreateSimpleWindow(x->display, x->root, c->x, c->y, 1, 1, BorderWidth, BorderColor, BackgroundColor);
- c->xft = XftDrawCreate(x->display, (Drawable) c->frame, x->visual, x->colormap);
- XSetWindowAttributes pattr = { .override_redirect = True };
- XChangeWindowAttributes(x->display, c->frame, CWOverrideRedirect, &pattr);
- XSetWindowBorder(x->display, c->win, BorderColor);
- XSetWindowBorderWidth(x->display, c->win, BorderWidth);
-
- /* setup event handling on both windows */
- XSelectInput(x->display, c->frame,
- ButtonPressMask|ButtonReleaseMask|FocusChangeMask|StructureNotifyMask);
- XSelectInput(x->display, c->win,
- EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
-
- /* position the window and frame */
- client_add(c, Desktop);
- client_config(x, c, c->x, c->y, c->w, c->h);
- client_redraw(x, c);
- client_raise(x, c);
-
- XSync(x->display, False);
- XUngrabServer(x->display);
-}
-
-static void client_destroy(XConf* x, Client* c)
-{
- client_del(c);
- XGrabServer(x->display);
- XftDrawDestroy(c->xft);
- XDestroyWindow(x->display, c->frame);
- xfree(c->name);
- free(c);
- XSync(x->display, False);
- XUngrabServer(x->display);
-}
-
-static Client* client_find(Window win)
-{
- struct { Window w; Client* c; } state = { .w = win };
- list_reduce(All_Clients, find_win, &state);
- return state.c;
-}
-
-static void client_resize(XConf* x, Client* c, int dir)
-{
- (void)x, (void)c, (void)dir;
- if (!c->tnode.prev) return;
- Client* prev = TCLIENT(c->tnode.prev);
- dir = (!abs(dir) ? -BARHEIGHT(x) : dir);
- int sh = HeightOfScreen(DefaultScreenOfDisplay(x->display));
- int miny = prev->y + BARHEIGHT(x);
- int maxy = (c->tnode.next ? TCLIENT(c->tnode.next)->y : sh) - BARHEIGHT(x);
- int newy = min(max(miny, (c->y + dir)), maxy);
- prev->h = newy - prev->y;
- c->h = (c->tnode.next ? (TCLIENT(c->tnode.next)->y - newy) : (sh - newy));
- c->y = newy;
- client_reconfig(x, prev);
- client_reconfig(x, c);
-}
-
-static void client_grow(XConf* x, Client* c)
-{
- GrowState state = {
- .c = c,
- .nclients = list_count(Tiled_Clients),
- .sh = HeightOfScreen(DefaultScreenOfDisplay(x->display))
- };
- list_reduce(Tiled_Clients, grow_client, &state);
-}
-
-static void client_maximize(XConf* xs, Client* c)
-{
- int y = c->y, h = c->h;
- c->y = 0, c->h = HeightOfScreen(DefaultScreenOfDisplay(xs->display));
- client_reconfig(xs, c);
- client_raise(xs, c);
- c->y = y, c->h = h;
-}
-
-/* Desktop Management
- *****************************************************************************/
-static void* hide_wins(Node* node, void* data)
-{
- Client* c = TCLIENT(node);
- XUnmapWindow(X.display, c->frame);
- XUnmapWindow(X.display, c->win);
- return data;
-}
-
-static void* show_wins(Node* node, void* data)
-{
- Client* c = TCLIENT(node);
- XMapWindow(X.display, c->frame);
- XMapWindow(X.display, c->win);
- client_redraw(&X, c);
- return data;
-}
-
-static void change_desktop(const Arg arg)
-{
- XGrabServer(X.display);
- list_reduce(Tiled_Clients, hide_wins, NULL);
- list_reduce(Floated_Clients, hide_wins, NULL);
- Desktop = arg.i;
- list_reduce(Tiled_Clients, show_wins, NULL);
- list_reduce(Floated_Clients, show_wins, NULL);
- XUngrabServer(X.display);
- XSync(X.display, False);
-}
-
-static void client_to_desktop(const Arg arg)
-{
- int bar;
- Window foo, win;
- do
- {
- (void)XQueryPointer(X.display, DefaultRootWindow(X.display),
- &foo, &win, &bar, &bar, &bar, &bar, (unsigned int*)&bar);
- }
- while (win <= 0);
- Client* c = client_find(win);
- if (!c) return;
- client_del(c);
- XUnmapWindow(X.display, c->frame);
- XUnmapWindow(X.display, c->win);
- XSync(X.display, False);
- client_add(c, arg.i);
-}
-
-/* X11 Event Handling
- *****************************************************************************/
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
-
-/*
- ** B1 Grow window a little
- ** B1 Drag: Resize vertically or move to column
- ** B2: Stack windows with titlebars visible but only one window expanded
- ** B3: Maximize in column
- * B1+B2: Kill window
- * Shift+B1: Move one column to the left
- * Shift+B2: Move one column to the right
-*/
-
-static void xbtnpress(XConf* x, XEvent* e)
-{
- Client* c = client_find(e->xbutton.window);
- if (!c || c->frame != e->xbutton.window || client_flags(c, FLOATING))
- return;
-
- if (Button1 == e->xbutton.button)
- {
- XDefineCursor(X.display, e->xbutton.window, Move_Cursor);
- StartY = e->xbutton.y;
- }
- else if (Button2 == e->xbutton.button)
- {
- if (e->xbutton.state & (1 << (Button1 + 7)))
- XDestroyWindow(x->display, e->xbutton.window);
- else
- client_grow(x, c);
- }
- else if (Button3 == e->xbutton.button)
- {
- client_maximize(x, c);
- }
-
- XSync(X.display, False);
-}
-
-static void xbtnrelease(XConf* x, XEvent* e)
-{
- Client* c = client_find(e->xbutton.window);
- if (!c || c->frame != e->xbutton.window || client_flags(c, FLOATING))
- return;
-
- if (Button1 == e->xbutton.button)
- {
- XUndefineCursor(X.display, e->xbutton.window);
- client_resize(x, c, e->xbutton.y - StartY);
- }
-
- XSync(X.display, False);
-}
-
-static void xconfigrequest(XConf* x, XEvent* e)
-{
- /*
- Check if it's a window we care about. If it is, and it's floating, just
- grant the request. Otherwise, deny it as we have it tiled already. All
- other windows have their requests granted.
- */
- Client* c = client_find(e->xconfigurerequest.window);
- XWindowChanges wc;
- wc.x = e->xconfigurerequest.x;
- wc.y = e->xconfigurerequest.y;
- wc.width = e->xconfigurerequest.width;
- wc.height = e->xconfigurerequest.height;
- wc.border_width = e->xconfigurerequest.border_width;
- wc.sibling = e->xconfigurerequest.above;
- wc.stack_mode = e->xconfigurerequest.detail;
- if (c && !client_flags(c, FLOATING))
- return;
- XConfigureWindow(x->display, e->xconfigurerequest.window, e->xconfigurerequest.value_mask, &wc);
-}
-
-static void xmaprequest(XConf* x, XEvent* e)
-{
- static XWindowAttributes attr = {0};
- if (XGetWindowAttributes(x->display, e->xmaprequest.window, &attr))
- {
- if (attr.override_redirect) return; /* ignore certain windows (like frames) */
- if (!client_find(e->xmaprequest.window))
- client_create(x, e->xmaprequest.window);
- }
-}
-
-static void xunmapnotify(XConf* x, XEvent* e)
-{
-}
-
-static void xdestroynotify(XConf* x, XEvent* e)
-{
- /* This is where we cleanup windows we care about. destroy them and their frames. */
- Client* c = client_find(e->xdestroywindow.window);
- if (c) client_destroy(x, c);
-}
-
-static void xclientmsg(XConf* x, XEvent* e)
-{
-}
-
-static void xpropnotify(XConf* x, XEvent* e)
-{
- /*
- We only care about updating the window titles here for now
- */
- Client* c = client_find(e->xproperty.window);
- if (c) {
- c->name = xfree(c->name);
- XFetchName(x->display, c->win, &c->name);
- client_redraw(x, c);
- }
-}
-
-static void xenternotify(XConf* x, XEvent* e)
-{
- /*
- Handle focus follows mouse here.
- */
-}
-
-static void xexpose(XConf* x, XEvent* e)
-{
- Client* c = client_find(e->xexpose.window);
- if (c && e->xexpose.count == 0)
- client_redraw(x, c);
-}
-
-#include <X11/XKBlib.h>
-static void xkeypress(XConf* x, XEvent* e)
-{
- KeySym keysym = XkbKeycodeToKeysym(
- x->display, e->xkey.keycode, 0, 0);
- for (size_t i = 0; i < nelem(keys); i++)
- if(keys[i].keysym == keysym && keys[i].mod == e->xkey.state)
- keys[i].function(keys[i].arg);
-}
-
-#pragma GCC diagnostic pop
-
-int main(void)
-{
- /* initialize basic x11 handling */
- x11_init(&X);
- X.font = x11_font_load(&X, Fonts[0]);
- XSelectInput(X.display, X.root, SubstructureRedirectMask|SubstructureNotifyMask);
- XSync(X.display, False);
- if (x11_error_get())
- die("Could not start. Is another WM running?\n");
-
- XA_NET_WM_WINDOW_TYPE = atom(&X, "_NET_WM_WINDOW_TYPE");
- XA_NET_WM_WINDOW_TYPE_DIALOG = atom(&X, "_NET_WM_WINDOW_TYPE_DIALOG");
- XA_WM_PROTOCOLS = atom(&X, "WM_PROTOCOLS");
-
- /* setup cursors */
- Main_Cursor = XCreateFontCursor(X.display, XC_left_ptr);
- Move_Cursor = XCreateFontCursor(X.display, XC_draped_box);
- XDefineCursor(X.display, X.root, Main_Cursor);
-
- KeyCode code;
- for (size_t i = 0; i < nelem(keys); i++)
- if ((code = XKeysymToKeycode(X.display, keys[i].keysym)))
- XGrabKey(X.display, code, keys[i].mod, X.root, True, GrabModeAsync, GrabModeAsync);
-
- /* register e vent handlers */
- X.eventfns[ButtonPress] = xbtnpress;
- X.eventfns[ButtonRelease] = xbtnrelease;
- X.eventfns[KeyPress] = xkeypress;
- X.eventfns[ConfigureRequest] = xconfigrequest;
- X.eventfns[MapRequest] = xmaprequest;
- X.eventfns[UnmapNotify] = xunmapnotify;
- X.eventfns[DestroyNotify] = xdestroynotify;
- X.eventfns[ClientMessage] = xclientmsg;
- X.eventfns[PropertyNotify] = xpropnotify;
- X.eventfns[EnterNotify] = xenternotify;
- X.eventfns[Expose] = xexpose;
- x11_event_loop(&X, 0);
- return 0;
-}
+++ /dev/null
-/**
- @file
- @ingroup tsed
-*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <regex.h>
-
-/* Line buffer flags */
-enum {
- LB_NEWLINE = (1 << 0),
- LB_DELETE = (1 << 1)
-};
-
-typedef struct {
- int flags;
- ssize_t capacity;
- ssize_t length;
- char* buffer;
-} LineBuf;
-
-/* Command flags */
-enum {
- SUB_GLOBAL = (1 << 0),
- SUB_PRINT = (1 << 1),
- IN_RANGE = (1 << 2),
-};
-
-typedef struct {
- enum { NONE, LINE, REGEX } type;
- union {
- regex_t* regex;
- size_t line;
- } u;
-} Addr;
-
-typedef struct {
- int type;
- int flags;
- Addr addr[2];
- regex_t* regex;
- char* text;
-} Cmd;
-
-typedef struct {
- size_t ncmds;
- size_t line;
- Cmd cmds[];
-} Prog;
-
-typedef void (*CmdFn)(Cmd* cmd, LineBuf* buf);
-
-static void lbputc(LineBuf* lbuf, int c)
-{
- if ((lbuf->length + 2u) >= lbuf->capacity)
- {
- lbuf->capacity += 2u;
- lbuf->buffer = realloc(lbuf->buffer, lbuf->capacity);
- }
- lbuf->buffer[lbuf->length++] = c;
- lbuf->buffer[lbuf->length] = '\0';
-}
-
-static void lbputsn(LineBuf* lbuf, char* s, ssize_t l)
-{
- for (ssize_t i = 0; i < l; i++)
- lbputc(lbuf, *(s+i));
-}
-
-static void cmd_d(Cmd* cmd, LineBuf* lbuf)
-{
- (void)cmd;
- lbuf->flags |= LB_DELETE;
-}
-
-static void cmd_s(Cmd* cmd, LineBuf* lbuf)
-{
- regmatch_t match[10];
- LineBuf out = {0};
- char* pos = lbuf->buffer;
- do {
- memset(match, 0, sizeof(match));
- if (!regexec(cmd->regex, pos, 10, match, 0))
- {
- lbputsn(&out, pos, match[0].rm_so);
- for (char* rs = cmd->text; *rs; rs++)
- {
- if (*rs == '\\')
- {
- rs++;
- if (isdigit(*rs))
- {
- int i = *rs - '0';
- lbputsn(&out, (pos + match[i].rm_so), (match[i].rm_eo - match[i].rm_so));
- }
- else
- {
- lbputc(&out, *rs);
- }
- }
- else
- {
- lbputc(&out, *rs);
- }
- }
- pos += match[0].rm_eo;
- if (!*pos || !(cmd->flags & SUB_GLOBAL))
- {
- lbputsn(&out, pos, (lbuf->length - (pos - lbuf->buffer) - 1));
- }
- }
- else
- {
- lbputsn(&out, pos, (lbuf->length - (pos - lbuf->buffer) - 1));
- break;
- }
- } while (*pos && (cmd->flags & SUB_GLOBAL));
- lbuf->length = 0;
- lbputsn(lbuf, out.buffer, out.length);
- free(out.buffer);
-}
-
-static const CmdFn Commands[] = {
- ['d'] = cmd_d,
- ['s'] = cmd_s
-};
-
-static int match_addr(Prog* prog, Addr* addr, LineBuf* lbuf)
-{
- if (addr->type == NONE)
- return 1;
- else if (addr->type == LINE)
- return (addr->u.line == prog->line);
- else if (addr->type == REGEX)
- return (regexec(addr->u.regex, lbuf->buffer, 0, 0, 0) == 0);
- else
- return 0;
-}
-
-static char* rdline(LineBuf* buf, FILE* file)
-{
- buf->length = getline(&(buf->buffer), (size_t*)&(buf->capacity), file);
- buf->flags = 0;
- if (buf->length <= 0) {
- free(buf->buffer);
- buf->buffer = NULL;
- } else if (buf->buffer[buf->length-1] == '\n') {
- buf->flags |= LB_NEWLINE;
- buf->buffer[buf->length-1] = '\0';
- }
- return buf->buffer;
-}
-
-static void parse_fail(char* msg)
-{
- printf("parse error: %s\n", msg);
- exit(1);
-}
-
-static char* parse_till(char* script, int term, char** str)
-{
- if (!*script) return script;
- char *sbeg = script, *send = script;
- for (; *send && *send != term; send++)
- if (*send == '\\') send++;
- *str = strndup(sbeg, send - sbeg);
- return (*send ? send+1 : send);
-}
-
-static char* parse_addr(char* script, Addr* addr)
-{
- if (isdigit(*script))
- {
- addr->type = LINE;
- for (; *script && isdigit(*script); script++)
- addr->u.line = (addr->u.line * 10) + (*script - '0');
- }
- else if (*script == '/')
- {
- addr->type = REGEX;
- char* rstr = NULL;
- script = parse_till(++script, '/', &rstr);
- addr->u.regex = calloc(1, sizeof(regex_t));
- if (regcomp(addr->u.regex, rstr, REG_EXTENDED|REG_NOSUB) != 0)
- parse_fail("failed to compile regex");
- }
- return script;
-}
-
-static char* parse_sub(char* script, Cmd* cmd)
-{
- int sep = *script++;
- if (!sep) return (script-1);
-
- /* parse out the regex and template */
- char *rstr = NULL, *tstr = NULL;
- script = parse_till(script, sep, &rstr);
- script = parse_till(script, sep, &tstr);
-
- /* parse the flags */
- for (;*script && !isspace(*script); script++)
- {
- switch (*script)
- {
- case 'g': cmd->flags |= SUB_GLOBAL; break;
- case 'p': cmd->flags |= SUB_PRINT; break;
- default: parse_fail("bad substitute flag");
- }
- }
-
- /* compile the regex and setup the command */
- cmd->text = tstr;
- cmd->regex = calloc(1, sizeof(regex_t));
- if (regcomp(cmd->regex, rstr, REG_EXTENDED|REG_NEWLINE) < 0)
- parse_fail("failed to compile regex");
- free(rstr);
-
- return script;
-}
-
-static Prog* prog_grow(Prog* prog, Cmd** lastcmd)
-{
- prog = realloc(prog, sizeof(Prog) + ((prog->ncmds + 1) * sizeof(Cmd)));
- *lastcmd = &(prog->cmds[prog->ncmds]);
- prog->ncmds++;
- memset(*lastcmd, 0, sizeof(Cmd));
- return prog;
-}
-
-Prog* prog_parse(char* script)
-{
- Cmd* cmd = NULL;
- Prog* prog = calloc(1, sizeof(Prog));
- while (*script) {
- while (isspace(*script)) script++;
- if (!*script) break;
-
- /* allocate a new command */
- prog = prog_grow(prog, &cmd);
-
- /* parse the addresses */
- script = parse_addr(script, &(cmd->addr[0]));
- if (*script == ',')
- script = parse_addr(++script, &(cmd->addr[1]));
-
- /* parse the command */
- cmd->type = *script;
- switch (*script++)
- {
- case 'd':
- /* handled above */
- break;
- case 's':
- script = parse_sub(script, cmd);
- break;
- case '\0':
- parse_fail("unexpected end of string");
- break;
- default:
- parse_fail("unexpected char");
- exit(1);
- break;
- }
- }
- return prog;
-}
-
-void prog_free(Prog* prog)
-{
-#define FREE_REGEX(regex) do { regfree(regex); free(regex); } while(0)
- for (size_t i = 0; i < prog->ncmds; i++) {
- if (prog->cmds[i].addr[0].type == REGEX) FREE_REGEX(prog->cmds[i].addr[0].u.regex);
- if (prog->cmds[i].addr[1].type == REGEX) FREE_REGEX(prog->cmds[i].addr[1].u.regex);
- if (prog->cmds[i].regex) FREE_REGEX(prog->cmds[i].regex);
- if (prog->cmds[i].text) free(prog->cmds[i].text);
- }
- free(prog);
-}
-
-void prog_exec(Prog* prog, LineBuf* lbuf)
-{
- prog->line++;
- for (size_t i = 0; i < prog->ncmds; i++)
- {
- Cmd* cmd = &(prog->cmds[i]);
- if ((cmd->flags & IN_RANGE) || match_addr(prog, &(cmd->addr[0]), lbuf))
- {
- if (cmd->addr[1].type != NONE)
- {
- cmd->flags |= IN_RANGE;
- if (match_addr(prog, &(cmd->addr[1]), lbuf))
- cmd->flags &= ~IN_RANGE;
- }
- Commands[cmd->type](cmd, lbuf);
- }
- }
-}
-
-int main(int argc, char** argv)
-{
- if (argc < 2) return 1;
-
- LineBuf buf = {0};
- Prog* prog = prog_parse(argv[1]);
- while (rdline(&buf, stdin))
- {
- prog_exec(prog, &buf);
- if (buf.flags & LB_DELETE) continue;
- fwrite(buf.buffer, 1u, buf.length, stdout);
- if (buf.flags & LB_NEWLINE)
- fwrite("\n", 1u, 1u, stdout);
- }
- prog_free(prog);
-
- return 0;
-}