libutil

C Utility Library
git clone http://git.omkov.net/libutil
Log | Tree | Refs | README | LICENCE | Download

libutil/src/mode.c (84 lines, 2.3 KiB) -rw-r--r-- blame download

01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
// util/mode.c, version 1.0.3
// Mode source file from libutil
// Copyright (C) 2020, Jakob Wakeling
// MIT Licence

/*
	TODO Handle copying permissions
*/

#include "mode.h"

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>

static inline int getop(char **s);
static inline mode_t getref(char **s);

/* Parse an octal or symbolic mode string */
chmod_t *strmode(char *str) {
	char *s = str; chmod_t *m; mode_t mode = 0; size_t i;
	
	// Try and parse an octal mode
	for (register mode_t d; *s >= '0' && *s <= '7'; ++s) {
		d = *s - '0'; if (mode > (07777 - d) / 8) { break; }
		mode = mode * 8 + d;
	}
	
	if (!*s) {
		m = (chmod_t *)malloc(2 * sizeof (*m));
		
		// Construct a chmod_t from the octal mode and return it
		m[0].flag = MF_NORM; m[1].flag = MF_NULL;
		m[0].op = '='; m[0].ref = M_ALL; m[0].mod = mode;
		
		return m;
	}
	
	// If the octal mode is invalid return NULL
	else if (s != str) { return NULL; }
	
	// Allocate necesary memory for chmod_t array
	for (i = 1; *s; ++s) { i += (*s == '+' || *s == '-' || *s == '='); }
	m = (chmod_t *)malloc((i + 1) * sizeof (*m)); s = str; m[i].flag = MF_NULL;
	
	i = 0; do { // Parse each part of the symbolic mode string
		for (mode_t ref = getref(&s); ((m[i].op = getop(&s))); ++i) {
			m[i].flag = MF_NORM; m[i].ref = ref;
			
			// Process the next mode
			for (m[i].mod = 0;;) switch (*s) {
				case 'r': { m[i].mod |= M_RD; ++s; continue; }
				case 'w': { m[i].mod |= M_WR; ++s; continue; }
				case 'x': { m[i].mod |= M_EX; ++s; continue; }
				case 'X': { m[i].flag = MF_XIFX; ++s; continue; }
				case 's': { m[i].mod |= M_ID; ++s; continue; }
				case 't': { m[i].mod |= M_ST; ++s; continue; }
				default: { goto end; }
			} end:;
		}
	} while (*s++ == ',' && *s);
	
	// If the symbolic mode is invalid return NULL
	if (*--s) { free(m); return NULL; } return m;
}

/* Process and return next operator */
static inline int getop(char **s) {
	switch (**s) { case '+': case '-': case '=': { return *(*s)++; }}
	return 0;
}

/* Process and return next reference */
static inline mode_t getref(char **s) {
	for (mode_t ref = 0;;) switch (**s) {
	case 'u': { ref |= M_USR; ++*s; continue; }
	case 'g': { ref |= M_GRP; ++*s; continue; }
	case 'o': { ref |= M_OTH; ++*s; continue; }
	case 'a': { ref |= M_ALL; ++*s; continue; }
	default: { return ref; }
	}
}