Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/imgfs/imgfs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <draw.h>

#include "mapbook.h"

#include "ifslib.h"
#include "import_ppm.h"
#include "convolute.h"
#include "imgfs.h"

File *
create_new_file(File *dir, char* filename, int perm, int id_code, Mapbook *m) {
	File *file;
	Aux *a;
	
	if (dir == nil || filename == nil) return nil;
	incref(dir);	/* so walk doesn't kill it immediately on failure */
	file = walkfile(dir, filename);
	if (file) {
		file = nil;
	} else {
		a = new_aux(id_code, m);
		file = createfile(dir, filename, getuser(), perm, &a->index);
		incref(dir);
	}

	decref(dir);
	
	return file;
}

void
add_channels(File* dir, Mapbook* m) {

	create_new_file(dir, "ctrl", 0666, id_ctrl, m);
	create_new_file(dir, "ppm", 0444, id_ppm, m);

	create_new_file(dir, "width", 0666, id_width, m);
	create_new_file(dir, "height", 0666, id_height, m);

	create_new_file(dir, "red", 0666, id_red, m);
	create_new_file(dir, "green", 0666, id_green, m);
	create_new_file(dir, "blue", 0666, id_blue, m);

	create_new_file(dir, "select", 0666, id_select, m);
	create_new_file(dir, "alpha", 0666, id_alpha, m);


// as yet unused but present for when required
// nothing to stop them being used

/*
	create_new_file(dir, "cyan", 0666, id_cyan, m);
	create_new_file(dir, "magenta", 0666, id_magenta, m);
	create_new_file(dir, "yellow", 0666, id_yellow, m);
	create_new_file(dir, "black", 0666, id_black, m);

	create_new_file(dir, "hue", 0666, id_cyan, m);
	create_new_file(dir, "saturation", 0666, id_magenta, m);
	create_new_file(dir, "lightness", 0666, id_yellow, m);
*/

}

Aux *
create_new_image_folder(char *filename) {
	Aux *a;
	File *file = walkfile(images_folder, filename);

	if (file)
		return nil;

	a = new_aux(id_imgfolder, nil);

	file = createfile(images_folder, filename, getuser(), DMDIR|0555, &a->index);
	
	incref(images_folder);

	add_channels(file, a->mapbook);

	return a;
}

Aux *
get_aux(void *indexp) {
	Aux *a = nil;
	if(indexp) {
		int index = *(int*)indexp;

		for(a = aux_list; a;  a = a->next) 
			if (a->index == index) break;

	}
	return a;
}

Aux *
new_aux(int id, Mapbook *m) {
	Aux *a;

	if (m == nil) {
		m = (Mapbook*) emallocz(sizeof(Mapbook));
		m->index = mapbook_count++;
	}

	if(aux_list == nil) {
		aux_list = (Aux*) emallocz (sizeof(Aux));
		aux_list->index = aux_count++;
		aux_list->id = id;
		aux_list->mapbook = m;
		return aux_list;
	}
	a = aux_list;

	while(a->next) a = a->next;

	a->next = (Aux*) emallocz (sizeof(Aux));
	a->next->id = id;
	a->next->index = aux_count++;
	a->next->mapbook = m;


	return a->next;
};

void
free_aux(Aux *a) {
	a->mapbook = free_mapbook(a->mapbook);
	free(a);
}

int
channel_to_id(char *channel) {
	if(strcmp("red", channel) == 0)
		return id_red;

	if(strcmp("green", channel) == 0)
		return id_green;

	if(strcmp("blue", channel) == 0)
		return id_blue;

	if(strcmp("cyan", channel) == 0)
		return id_cyan;

	if(strcmp("magenta", channel) == 0)
		return id_magenta;

	if(strcmp("yellow", channel) == 0)
		return id_yellow;

	if(strcmp("black", channel) == 0)
		return id_black;

	if(strcmp("hue", channel) == 0)
		return id_hue;

	if(strcmp("saturation", channel) == 0)
		return id_saturation;

	if(strcmp("lightness", channel) == 0)
		return id_lightness;

	if(strcmp("select", channel) == 0)
		return id_select;

	if(strcmp("alpha", channel) == 0)
		return id_alpha;

	return 0;
}

char *
process_ctl_message(Aux *aux, Req *r) {
	int t;
	char *tokens[4];
	int id;

	t = getfields(r->ifcall.data, tokens, 4, 1, " \t");

	switch(t) {
	case 3:
		if(strcmp("import", tokens[0]) == 0) {
			*(strchr(tokens[2], '\n')) = 0;
			if(strcmp("ppm", tokens[1]) == 0)
				return import_ppm(aux, tokens[2]);
		}
	case 4 :
		if (strcmp("convolute", tokens[0]) == 0) {
			id = channel_to_id(tokens[1]);
			if(id) {
				*(strchr(tokens[3], '\n')) = 0;
				return convolute(aux, id, tokens[2], tokens[3]);
			} else {
				fprint(2, "Invalid channel name\n");
			}
		}
		break;
	}
	return nil;
}

void
fswrite(Req *r) {
	Aux *aux ;
	Bitmap *target;
	char *errstr = nil;
	char *txt;
	vlong dataend;

	aux = get_aux(r->fid->file->aux);
	if(aux== nil) {
		respond(nil, "not found");
		return;
	}

	if(aux->id & 0xFF00) { // it's a channel
		target = get_or_create_bitmap(aux->mapbook, aux->id);

		dataend = aux->mapbook->width * aux->mapbook->height;
		if ((r->ifcall.offset + r->ifcall.count) > dataend) {
			respond(r, "too much data");
			return;
		}

		memcpy(&target->data[r->ifcall.offset], r->ifcall.data,  r->ifcall.count);
		r->ofcall.count = r->ifcall.count;

		respond(r, nil);
		return;
	}

	switch(aux->id) {
	case id_new : 
		if (r->ifcall.count > 0) {
			txt = (char*)emallocz(r->ifcall.count+1);
			memcpy(txt, r->ifcall.data, r->ifcall.count);
			txt[strcspn(txt, "\n/\0 ")] = 0;
			if(create_new_image_folder(txt))
				r->ofcall.count = r->ifcall.count;
			else
				errstr = smprint("create failed - file existed");
		}
		break;
	case id_width :
		errstr = set_mapbook_width(aux->mapbook, atoi(r->ifcall.data));
		break;
	case id_height :
		errstr = set_mapbook_height(aux->mapbook, atoi(r->ifcall.data));
		break;
	case id_ctrl :
		errstr = process_ctl_message(aux, r);
		break;
	}

	respond(r, errstr);
	free(errstr);
	
}

void
fill_ofcall_with_data(Fcall *ifcall, Fcall *ofcall, int datasize, char *data) {
	if (ifcall->offset < datasize) {
		if((ifcall->offset + ifcall->count) < datasize) {
			ofcall->data = data + ifcall->offset;
			ofcall->count = ifcall->count;
		} else {
			ofcall->data = data + ifcall->offset;
			ofcall->count = datasize - ifcall->offset;
		}
	} else {
		ofcall->data = nil;
		ofcall->count = 0;
	}
}

void
fill_ofcall_with_bitmap(Fcall *ifcall, Fcall *ofcall, Mapbook *m, int id) {
	Bitmap *b;
	long pixels;
	if(b = get_bitmap(m, id)) {
		pixels = m->width * m->height;
		fill_ofcall_with_data(ifcall, ofcall, pixels, (char*) b->data);
	} else {
		ofcall->data = nil;
		ofcall->count = 0;
	}
}

char *
ppm_header(Mapbook *m) {
	return smprint("P6 %06d %06d 255\n", m->width, m->height);
}

long
ppm_size(char *header, Mapbook *m) {
	return strlen(header) + 3 * m->width * m->height;
}

char *
fill_ofcall_with_ppm(Fcall *ifcall, Fcall *ofcall, Mapbook *m) {
	char *header;
	int header_size;

	long ppmsize, limit, req_length, start, pixel, end_pixel;
	char *insert_point;
	Bitmap *r, *g, *b;

	ofcall->data = nil;
	ofcall->count = 0;

	if(!m) return ofcall->data;

	header = ppm_header(m);
	header_size = strlen(header);
	ppmsize = ppm_size(header, m);

	if(ifcall->offset < ppmsize) {
		r = get_bitmap(m, id_red);
		g = get_bitmap(m, id_green);
		b = get_bitmap(m, id_blue);

		req_length = ifcall->offset + ifcall->count;
		limit = (req_length < ppmsize) ? req_length : ppmsize;
		req_length = limit - ifcall->offset;

		insert_point = ofcall->data = (char*) emallocz(req_length);
		start = ifcall->offset;

		if(start < header_size) {
			ofcall->count = header_size - start;
			if (ofcall->count > ifcall->count)
				ofcall->count = ifcall->count;
			
			memcpy(insert_point, &header[start], ofcall->count);
			 insert_point = &ofcall->data[ofcall->count];
		} else
			ofcall->count = 0;

		if(ofcall->count == ifcall->count) return ofcall->data;

		req_length -= ofcall->count;
		start = ifcall->offset + ofcall->count - header_size;

		pixel = start / 3;
/*
reads always finish on a pixel boundary so that the next read starts at a pixel boundary
though I have no proof that it *always* works
	this switch was supposed deal with the boundaries but it was always unused so I commented it out

		switch(start % 3) {
		case 1 :
			insert_point[0] = r->data[pixel-1];
			insert_point = &insert_point[1];
			ofcall->count++;
			req_length--;
			if (req_length == 0) return ofcall->data;
		case 2:
			insert_point[0] = b->data[pixel-1];
			insert_point = &insert_point[1];
			ofcall->count++;
			req_length--;
			if (req_length == 0) return ofcall->data;
		}

*/
		end_pixel = pixel + req_length / 3;

		for(; pixel < end_pixel; pixel++) {
			insert_point[0] = r->data[pixel];
			ofcall->count++;
			req_length--;
			if (req_length == 0) return ofcall->data;
			insert_point[1] = g->data[pixel];
			ofcall->count++;
			req_length--;
			if (req_length == 0) return ofcall->data;
			insert_point[2] = b->data[pixel];
			ofcall->count++;
			req_length--;
			if (req_length == 0) return ofcall->data;

			insert_point = &insert_point[3];
		}
	// reads always finish on a pixel boundary so that the next read starts at a pixel boundary
	// similarly there was another switch here, it was wrong so I deleted it
	}

	return ofcall->data;
}


void
fsread(Req *r)
{
	Aux *aux;
	char *data=nil;
	aux = get_aux(r->fid->file->aux);
	if(!aux) {
		respond(r, "not found");
		return;
	}
	
	switch(aux->id){
	case id_width :
		data = smprint("%d", aux->mapbook->width);
		readstr(r, data);
		break;
	case id_height :
		data = smprint("%d", aux->mapbook->height);
		readstr(r, data);
		break;
	case id_ppm :
		data = fill_ofcall_with_ppm(&r->ifcall, &r->ofcall, aux->mapbook);
		break;
	default :
		if(aux->id & 0xFF00) // it's a channel
			fill_ofcall_with_bitmap(&r->ifcall, &r->ofcall, aux->mapbook, aux->id);
	} 

	respond(r, nil);
	free(data);
}

void
fsend (Srv *) {
	Aux *aux, *next;

	for(aux = aux_list; aux; aux = next) {
		next = aux->next;
		free_aux(aux);
	}
	aux_list = nil;
}

void
fstati(Req *r) {
	Aux *a = get_aux(r->fid->file->aux);
	char *data;
	if(a)
		switch(a->id) {
		case id_width :
			data = smprint("%d", a->mapbook->width);
			r->d.length = strlen(data);
			free(data);
			break;
		case id_height :
			data = smprint("%d", a->mapbook->height);
			r->d.length = strlen(data);
			free(data);
			break;
		case id_ppm :
			data = ppm_header(a->mapbook);
			r->d.length = ppm_size(data, a->mapbook);
			free(data);
			break;
		default :
			if(a->id & 0xFF00) { // it's a channel
				Mapbook *m = (Mapbook*) (a->mapbook);
				if(m) {
					if(get_bitmap(m, a->id)) {
						r->d.length = m->width * m->height;
					}
				}
			}
			break;
		}

	respond(r, nil);
}


Srv fs = 
{
.read=		fsread,
.write=		fswrite,
.end=		fsend,
.stat=		fstati,
};


static void
usage(void)
{
	fprint(2, "usage: imgfs [-n mtpt] \n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	char *mtpt;
	
	mtpt = "/n/imgfs";

	ARGBEGIN{
	case 'n':
		mtpt = ARGF();
		break;
	}ARGEND;
	if(argc != 0) usage();
	tree = fs.tree = alloctree(getuser(), getuser(), DMDIR|0555, nil);
	create_new_file(tree->root, "new", 0666, id_new, nil);
	images_folder = create_new_file(tree->root, "images", DMDIR|0777, id_images_folder, nil);
	postmountsrv(&fs, nil, mtpt, MREPL);

	exits(nil);

}


/*
	Put

	mk install

imgfs/imgfs
cd  /n/imgfs
echo arab > new
cd images/arab
imgfs/import_ppm /usr/maht/imgfs/arabian.ppm
ls -l *

wc ppm

*/

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.