Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/limbo/tabfs1.b

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


# Copyright M Heath 2003
# proof of concept code or tabfs

# takes a tab delimited file and presents it as a file system with the first row as column names and the first column as the primary key
# it will bork if these criteria are not met

# hope you've got plenty of memory, you're probably going to need it

implement tabfs1;
include "sys.m";
include "draw.m";
include "styx.m";
include "styxservers.m";
include "bufio.m";
include "string.m";
include "arg.m";

arg: Arg;
# sys
sys: Sys;
str : String;
# styx stuff

styx : Styx;
bufio : Bufio;
Rmsg : import styx;
styxservers: Styxservers;
Styxserver, Navigator: import styxservers;
nametree: Nametree;
Tree: import nametree;
tree : ref Tree;
Iobuf: import bufio;
# module definition

tabfs1 : module
{
	init: fn(nil: ref Draw->Context, argv: list of string);
};


# my adts

src_file : adt {
		filename : array of byte;
		column_names : array of byte;
};


# other globals 

bitshift : con 32;
Qroot, Qnew, Qmeta_folder, Qdata_folder, Qcolumn_names, Qfilename, Qctl, Qcolumn, Qrow, Qnumber_of_rows, Q: con big iota;	# paths

src_files : array of src_file ;
file_count := 0;

columns : array of array of byte;
column_count := 0;



init(nil: ref Draw->Context, args : list of string)
{
     	sys = load Sys Sys->PATH;

         arg = load Arg Arg->PATH;

	f : string;
	starting_filename : string;

# kludge some defaults in;
	primary_key_column := 0;
	column_names_in_first_row := 1;

	arg->init(args);
         	while((c := arg->opt()) != 0)
             	case c {
             	'p' => (primary_key_column, nil) = str->toint(arg->arg(), 10);
             	'c' => column_names_in_first_row = 1;
		'f' => starting_filename = arg->arg();
             	* =>   sys->print("unknown option (%c)\n", c);
         }

	if(f == nil) {
#		sys->print("need a source filename that has tab separated columns\n");
#		exit;
	}

	
     
     	styx = load Styx Styx->PATH;
	bufio = load Bufio Bufio->PATH;
	treeop : chan of ref Styxservers->Navop;
	str = load String String->PATH;
     	styx->init();
     	styxservers = load Styxservers Styxservers->PATH;
     	styxservers->init(styx);
     	nametree = load Nametree Nametree->PATH;
     	nametree->init();
     	sys->pctl(Sys->FORKNS, nil);
     	(tree, treeop) = nametree->start();
     	tree.create(Qroot, dir(".", 8r555|Sys->DMDIR, Qroot));

  	tree.create(Qroot, dir("new", 8r666, Qnew));

	if(starting_filename != nil) 
		add_src_file(array of byte starting_filename, column_names_in_first_row, primary_key_column);

   	(tchan, srv) := Styxserver.new(sys->fildes(0),Navigator.new(treeop), Qroot);
	reply : ref Rmsg;

     	while((gm := <-tchan) != nil) {

		pick m := gm {
			Read => {
				fid := srv.getfid(m.fid);
				qtype := big int(fid.path);
				case (qtype) {
					Qcolumn_names => {
						i := int(fid.path >> bitshift);

						reply = styxservers->readbytes(m, src_files[i].column_names);
					}
					Qfilename => {
						i := int(fid.path >> bitshift);
						reply = styxservers->readbytes(m, src_files[i].filename);
					}

					Qcolumn => {
						i := int(fid.path >> bitshift);
						reply = styxservers->readbytes(m, columns[i]);
					}
				}
			}

			Write => {
				fid := srv.getfid(m.fid);
				qtype := big int(fid.path);
				case (qtype) {
					Qnew => {
						add_src_file(m.data, column_names_in_first_row, primary_key_column);
						reply = ref Rmsg.Write(m.tag, len m.data);
					}
					Qctl => { # doesn't do anything yet
						data := m.data;
						reply = ref Rmsg.Write(m.tag, len m.data);
					}
				}
			}

			Remove => {
				fid := srv.getfid(m.fid);
				qtype := big int(fid.path);
				case (qtype) {
					Qcolumn => {
						i := int(fid.path >> bitshift);
						columns[i] = nil;
						reply = ref Rmsg.Remove(m.tag);
					}
				}

				if (reply != nil) {
					srv.delfid(fid);
				}
			}
		}

		if(reply == nil) {
			srv.default(gm);
		} else {
			srv.reply(reply);
			reply = nil;
		}
	}
	tree.quit();
}

dir(name: string, perm: int, qid: big): Sys->Dir {
	d := sys->zerodir;
     	d.name = name;
	d.uid = "inferno";
	d.gid = "inferno";
	d.qid.path = qid;
	if (perm & Sys->DMDIR)
		d.qid.qtype = Sys->QTDIR;
	else
		d.qid.qtype = Sys->QTFILE;
	d.mode = perm;
	return d;
}

extend_files() { # grow the files array
	quarter := 5 + len src_files / 4;  # formula plucked from thin air

	new_files := array[len src_files + quarter] of src_file;

	if (len src_files > 0)
		new_files[0:] = src_files;

	src_files = new_files;
}
	
add_src_file(name : array of byte, use_first_row_as_column_names, primary_key_column : int) {
	if (file_count == len src_files) 
		extend_files();

	src_files[file_count] = src_file(name, nil);
	add_meta_folder();
	read_rows(use_first_row_as_column_names, primary_key_column);

	file_count++;
}


add_meta_folder() {
	bfc := big file_count;
	bfc = bfc << bitshift;

	m_qid := bfc + Qmeta_folder;
	d_qid := bfc + Qdata_folder;

	tree.create(Qroot, dir(sys->sprint("%d", file_count), 8r755 | Sys->DMDIR, m_qid));

	tree.create(m_qid, dir("data", 8r555 | Sys->DMDIR, d_qid));
	tree.create(m_qid, dir("src_filename", 8r444, bfc + Qfilename));
	tree.create(m_qid, dir("column_names", 8r444, bfc + Qcolumn_names));
	tree.create(m_qid, dir("number_of_rows", 8r444, bfc + Qnumber_of_rows));
	tree.create(m_qid, dir("ctl", 8r644, bfc +Qctl));
}

set_column_names_from_row(data : string) : int { # return the number of entries added I suppose -1 for fail, 

	if(data == "") return 0;
	
	(column_row, nil) := str->splitl(data, "\n");

	src_files[file_count].column_names = array of byte column_row;

	return 1;
}

set_column_names_to_numbers(data : string) :  int { # return the number of entries added I suppose -1 for fail, 

	if(data == "") return 0;

	(column_row, nil) := str->splitl(data, "\n");
	
	num_columns := len split_on_tabs(column_row);

	row := "0";

	for(i := 1; i < num_columns; i++)
		row += sys->sprint("\t%d", i);

	

	return set_column_names_from_row(row);
}

set_column_names_and_get_first_row(data_buf : ref Bufio->Iobuf, column_names_in_first_row : int) : string { 
	row :=  data_buf.gets('\n');

	if(column_names_in_first_row) {
		set_column_names_from_row(row); 
		row =  data_buf.gets('\n');
	} else {
		set_column_names_to_numbers(row);
	}

	return row;
}

get_column_names_as_array() : array of string {
	return list_to_array(split_on_tabs(string src_files[file_count].column_names));
}

list_to_array(lizt : list of string) : array of string {
	num_columns := len lizt;
	hooray := array[num_columns] of string;
	for(i := num_columns -1;  i > -1 ; i--) {
		hooray[i] = hd lizt;
		lizt = tl lizt;
	}

	return hooray;
}

read_rows(column_names_in_first_row, primary_key_column : int) : int { # return number of rows
	bfc := big file_count;
	bfc = bfc << bitshift;

	d_qid := bfc + Qdata_folder;


	row : string;
	num_rows := 0;

	data_buf := bufio->open(string src_files[file_count].filename, Bufio->OREAD);

	row = set_column_names_and_get_first_row(data_buf, column_names_in_first_row);
	
	column_name_lookup := get_column_names_as_array() ;

	if (primary_key_column > len column_name_lookup)
		primary_key_column = -1;
		
	fields : array of string;
	row_folder : big;
	pk_value : string;

	while(row != nil) {
		fields = list_to_array(split_on_tabs(row));

		if(primary_key_column < 0)
			pk_value = sys->sprint("%d", num_rows);
		else
			pk_value = string fields[primary_key_column];

		if(pk_value == "\n") { 
			row = data_buf.gets('\n');
			continue;
		}
		
		bfl := big num_rows;
		bfl = bfl << bitshift;
		
		row_folder = bfl + Qrow;
		tree.create(d_qid, dir(pk_value, 8r555|Sys->DMDIR, row_folder));

		min_fields := min(len fields, len column_name_lookup);  # being generous I'll read up to a maximum of the number of columns in the first row 
		
		for(j := 0; j < min_fields; j++)
			add_column(row_folder, column_name_lookup[j], fields[j]);

		num_rows++;

		row = data_buf.gets('\n');
	}

	return num_rows;
}

add_column(parent : big, name : string, data : string) {
	if (column_count == len columns) 
		extend_columns();

	columns[column_count] = array of byte data;

	bcc := big column_count;
	bcc = bcc << bitshift;

	tree.create(parent, dir(name, 8r444, bcc + Qcolumn));

	column_count++;
}

extend_columns() {
	quarter := 5 + len columns / 4;  # formula plucked from thin air

	new_columns := array[len columns + quarter] of array of byte;

	if (len columns > 0)
		new_columns [0:] = columns;

	columns = new_columns ;
}

min(a, b : int) : int {
	if(a < b) return a;
	return b;
}

split_on_tabs(row : string) : list of string {

	bits : list of string;

	g := 0;

	for(d:=0; d < len row; d++) {
		if (row[d] == '\t') {
			bits = row[g:d] :: bits;
			g = d + 1;
		}
	}

	if(d) bits = row[g:d] :: bits;
	
	return bits;
}

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.