Add compiler and vm-translator
This commit is contained in:
180
misc/io.c
Normal file
180
misc/io.c
Normal file
@@ -0,0 +1,180 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include "util.h"
|
||||
#include "io.h"
|
||||
|
||||
#include <limits.h>
|
||||
#ifndef PATH_MAX
|
||||
#ifdef __linux__
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#define PATH_MAX 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char* strtail(char* str, int len, int count) {
|
||||
int index = len - count;
|
||||
if (index <= 0) return str;
|
||||
return str + (sizeof(char) * (index));
|
||||
}
|
||||
|
||||
char* strhead(char* str, int count) {
|
||||
return str + (sizeof(char) * count);
|
||||
}
|
||||
|
||||
char* trimstr(char* str, int len, int end) {
|
||||
int count = len - end;
|
||||
char oldchar = str[count];
|
||||
str[count] = '\0';
|
||||
char* newstr = (char*)malloc(sizeof(char) * (1 + count));
|
||||
strcpy(newstr, str);
|
||||
str[count] = oldchar;
|
||||
return newstr;
|
||||
}
|
||||
|
||||
char* getname(char* f, int len) {
|
||||
int startind = 0;
|
||||
int endind = len - 1;
|
||||
bool readsmt = false;
|
||||
|
||||
for(int i = endind; i >= 0; i--) {
|
||||
if(f[i] == '/') {
|
||||
if(!readsmt) {
|
||||
endind = i-1;
|
||||
f[i] = '\0';
|
||||
continue;
|
||||
}
|
||||
startind = i+1;
|
||||
break;
|
||||
}
|
||||
readsmt = true;
|
||||
}
|
||||
|
||||
int sz = sizeof(char)*(endind - startind + 2);
|
||||
char* startstr = strhead(f, startind);
|
||||
char* retstr = (char*)malloc(sz);
|
||||
snprintf(retstr, sz, "%s", startstr);
|
||||
return retstr;
|
||||
}
|
||||
|
||||
char* getfullname(char* fname, int fnamelen, char* dirname, int dirlen) {
|
||||
int sz = sizeof(char)*(fnamelen+dirlen+2);
|
||||
char* fullname = (char*)malloc(sz);
|
||||
sprintf(fullname, "%s/%s", dirname, fname);
|
||||
return fullname;
|
||||
}
|
||||
|
||||
bool isdotjack(char* f, int len) {
|
||||
const char* ext = ".jack";
|
||||
return strcmp(strtail(f, len, strlen(ext)), ext) == 0;
|
||||
}
|
||||
|
||||
bool isdir(char* f, int len) {
|
||||
bool readsmt = false;
|
||||
for(int i = len-1; i >= 0; i--) {
|
||||
if(f[i] == '.') {
|
||||
if(readsmt)
|
||||
return false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if(f[i] == '/')
|
||||
return 1;
|
||||
readsmt = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
char* getoutname(char* fullname, int len) {
|
||||
char* trimmed = trimstr(fullname, len, 4);
|
||||
int sz = sizeof(char) * (len);
|
||||
char* outname = (char*)malloc(sz);
|
||||
snprintf(outname, sz, "%sasm", trimmed);
|
||||
free(trimmed);
|
||||
return outname;
|
||||
}
|
||||
|
||||
FILELIST* addfile(FILELIST* l, char* fullname, char* name) {
|
||||
FILELIST* new = (FILELIST*)malloc(sizeof(FILELIST));
|
||||
new->name = name;
|
||||
new->fullname = fullname;
|
||||
new->next = l;
|
||||
new->outname = getoutname(fullname, strlen(fullname));
|
||||
return new;
|
||||
}
|
||||
|
||||
FILELIST* getfilesfromdir(char* dir) {
|
||||
FILELIST* filelist = NULL;
|
||||
DIR* d = opendir(dir);
|
||||
|
||||
if(d == NULL) {
|
||||
eprintf("Error while opening directory '%s': %s\n", dir, strerror(errno));
|
||||
exit(errno);
|
||||
}
|
||||
|
||||
int len = strlen(dir);
|
||||
struct dirent* thisfile;
|
||||
while(thisfile = readdir(d), thisfile != NULL) {
|
||||
int thislen = strlen(thisfile->d_name);
|
||||
if(isdotjack(thisfile->d_name, thislen)) {
|
||||
char* fullname = getfullname(thisfile->d_name, thislen, dir, len);
|
||||
char* name = ezheapstr(thisfile->d_name);
|
||||
filelist = addfile(filelist, fullname, name);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
|
||||
if(filelist == NULL) {
|
||||
eprintf("Directory '%s' doesn't have any .jack file\n", dir);
|
||||
exit(1);
|
||||
}
|
||||
return filelist;
|
||||
}
|
||||
|
||||
FILELIST* getsinglefile(char* file) {
|
||||
int len = strlen(file);
|
||||
if(isdotjack(file, len)){
|
||||
char* name = getname(file, len);
|
||||
char* fullname = heapstr(file, len);
|
||||
|
||||
FILE* input = fopen(fullname, "r");
|
||||
if(input == NULL) {
|
||||
eprintf("Error while reading file '%s': %s\n", file, strerror(errno));
|
||||
exit(errno);
|
||||
}
|
||||
fclose(input);
|
||||
|
||||
return addfile(NULL, fullname, name);
|
||||
}
|
||||
else {
|
||||
eprintf("Input file must be named like 'Xxx.vm'\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
FILELIST* getfiles(char* input) {
|
||||
int inplen = strlen(input);
|
||||
bool isitdir = isdir(input, inplen);
|
||||
|
||||
if(isitdir)
|
||||
return getfilesfromdir(input);
|
||||
else
|
||||
return getsinglefile(input);
|
||||
}
|
||||
|
||||
void freefilelist(FILELIST* fs) {
|
||||
free(fs->name);
|
||||
free(fs->fullname);
|
||||
free(fs->outname);
|
||||
FILELIST* next = fs->next;
|
||||
free(fs);
|
||||
if(next != NULL)
|
||||
freefilelist(next);
|
||||
}
|
14
misc/io.h
Normal file
14
misc/io.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
typedef struct flist {
|
||||
char* name;
|
||||
char* fullname;
|
||||
char* outname;
|
||||
struct flist* next;
|
||||
} FILELIST;
|
||||
|
||||
|
||||
FILELIST* getfiles(char* input);
|
||||
void freefilelist(FILELIST* fs);
|
||||
#endif
|
166
misc/os.c
Normal file
166
misc/os.c
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "os.h"
|
||||
#include "util.h"
|
||||
|
||||
CLASS* mkosclass(CLASS* os, const char* name) {
|
||||
CLASS* c = (CLASS*)malloc(sizeof(CLASS));
|
||||
c->name = ezheapstr(name);
|
||||
c->subroutdecs = NULL;
|
||||
c->next = os;
|
||||
return c;
|
||||
}
|
||||
|
||||
void adddec(CLASS* c, SUBROUTCLASS subroutclass, char* type, const char* name) {
|
||||
SUBROUTDEC* dec = (SUBROUTDEC*)malloc(sizeof(SUBROUTDEC));
|
||||
dec->class = c;
|
||||
dec->subroutclass = subroutclass;
|
||||
dec->name = ezheapstr(name);
|
||||
dec->type = ezheapstr(type);
|
||||
dec->next = c->subroutdecs;
|
||||
c->subroutdecs = dec;
|
||||
}
|
||||
|
||||
CLASS* mkmath(CLASS* os) {
|
||||
CLASS* mathclass = mkosclass(os, "Math");
|
||||
adddec(mathclass, function, "int", "multiply");
|
||||
adddec(mathclass, function, "int", "divide");
|
||||
adddec(mathclass, function, "int", "abs");
|
||||
adddec(mathclass, function, "int", "min");
|
||||
adddec(mathclass, function, "int", "max");
|
||||
adddec(mathclass, function, "int", "sqrt");
|
||||
return mathclass;
|
||||
}
|
||||
|
||||
CLASS* mkstringclass(CLASS* os) {
|
||||
CLASS* strclass = mkosclass(os, "String");
|
||||
adddec(strclass, constructor, "String", "new");
|
||||
adddec(strclass, method, "int", "dispose");
|
||||
adddec(strclass, method, "int", "length");
|
||||
adddec(strclass, method, "char", "charAt");
|
||||
adddec(strclass, method, "void", "setCharAt");
|
||||
adddec(strclass, method, "String", "appendChar");
|
||||
adddec(strclass, method, "void", "eraseLastChar");
|
||||
adddec(strclass, method, "int", "intValue");
|
||||
adddec(strclass, method, "void", "setInt");
|
||||
adddec(strclass, function, "char", "backSpace");
|
||||
adddec(strclass, function, "char", "doubleQuote");
|
||||
adddec(strclass, function, "char", "newLine");
|
||||
return strclass;
|
||||
}
|
||||
|
||||
CLASS* mkarray(CLASS* os) {
|
||||
CLASS* arrclass = mkosclass(os, "Array");
|
||||
adddec(arrclass, function, "Array", "new");
|
||||
adddec(arrclass, method, "void", "dispose");
|
||||
return arrclass;
|
||||
}
|
||||
|
||||
CLASS* mkoutput(CLASS* os) {
|
||||
CLASS* outclass = mkosclass(os, "Output");
|
||||
adddec(outclass, function, "void", "moveCursor");
|
||||
adddec(outclass, function, "void", "printChar");
|
||||
adddec(outclass, function, "void", "printString");
|
||||
adddec(outclass, function, "void", "printInt");
|
||||
adddec(outclass, function, "void", "println");
|
||||
adddec(outclass, function, "void", "backSpace");
|
||||
return outclass;
|
||||
}
|
||||
|
||||
CLASS* mkscreen(CLASS* os) {
|
||||
CLASS* scrclass = mkosclass(os, "Screen");
|
||||
adddec(scrclass, function, "void", "clearScreen");
|
||||
adddec(scrclass, function, "void", "setColor");
|
||||
adddec(scrclass, function, "void", "drawPixel");
|
||||
adddec(scrclass, function, "void", "drawLine");
|
||||
adddec(scrclass, function, "void", "drawRectangle");
|
||||
adddec(scrclass, function, "void", "drawCircle");
|
||||
return scrclass;
|
||||
}
|
||||
|
||||
CLASS* mkkeyboard(CLASS* os) {
|
||||
CLASS* kbdclass = mkosclass(os, "Keyboard");
|
||||
adddec(kbdclass, function, "char", "keyPressed");
|
||||
adddec(kbdclass, function, "char", "readChar");
|
||||
adddec(kbdclass, function, "String", "readLine");
|
||||
adddec(kbdclass, function, "int", "readInt");
|
||||
return kbdclass;
|
||||
}
|
||||
|
||||
CLASS* mkmemory(CLASS* os) {
|
||||
CLASS* memclass = mkosclass(os, "Memory");
|
||||
adddec(memclass, function, "int", "peek");
|
||||
adddec(memclass, function, "void", "poke");
|
||||
adddec(memclass, function, "Array", "alloc");
|
||||
adddec(memclass, function, "void", "deAlloc");
|
||||
return memclass;
|
||||
}
|
||||
|
||||
CLASS* mksys(CLASS* os) {
|
||||
CLASS* sysclass = mkosclass(os, "Sys");
|
||||
adddec(sysclass, function, "void", "halt");
|
||||
adddec(sysclass, function, "void", "error");
|
||||
adddec(sysclass, function, "void", "wait");
|
||||
return sysclass;
|
||||
}
|
||||
|
||||
CLASS* mkos() {
|
||||
CLASS* os = mkmath(NULL);
|
||||
os = mkstringclass(os);
|
||||
os = mkarray(os);
|
||||
os = mkoutput(os);
|
||||
os = mkscreen(os);
|
||||
os = mkkeyboard(os);
|
||||
os = mkmemory(os);
|
||||
os = mksys(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
void freeossubroutdecs(SUBROUTDEC* d) {
|
||||
free(d->name);
|
||||
free(d->type);
|
||||
SUBROUTDEC* next = d->next;
|
||||
free(d);
|
||||
if(next != NULL)
|
||||
freeossubroutdecs(next);
|
||||
}
|
||||
|
||||
void freeosclasses(CLASS* c) {
|
||||
freeossubroutdecs(c->subroutdecs);
|
||||
free(c->name);
|
||||
CLASS* next = c->next;
|
||||
free(c);
|
||||
if(next != NULL)
|
||||
freeosclasses(next);
|
||||
}
|
||||
|
||||
void freeos(CLASS* os) {
|
||||
freeosclasses(os);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecinclass(CLASS* c, const char* name) {
|
||||
SUBROUTDEC* curr = c->subroutdecs;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CLASS* getosclass(CLASS* os, const char* name) {
|
||||
CLASS* curr = os;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getossubroutdec(CLASS* os, SUBROUTCALL* call) {
|
||||
CLASS* c = getosclass(os, call->parentname);
|
||||
if(c == NULL)
|
||||
return NULL;
|
||||
return getsubroutdecinclass(c, call->name);
|
||||
}
|
10
misc/os.h
Normal file
10
misc/os.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef OS_H
|
||||
#define OS_H
|
||||
#include "parser-tree.h"
|
||||
|
||||
SUBROUTDEC* getossubroutdec(CLASS* os, SUBROUTCALL* call);
|
||||
CLASS* getosclass(CLASS* os, const char* name);
|
||||
CLASS* mkos();
|
||||
void freeos(CLASS* os);
|
||||
|
||||
#endif
|
102
misc/threads.c
Normal file
102
misc/threads.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "threads.h"
|
||||
|
||||
void* parseunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
unit->parsed = parse(unit->parser);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void* compileunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
unit->compiled = compileclass(unit->compiler, unit->parsed);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
char* getclassname(char* filename) {
|
||||
int count = 0;
|
||||
int len = strlen(filename);
|
||||
|
||||
for(int i = len-1; i >= 0; i--)
|
||||
if(filename[i] == '.') {
|
||||
count = i;
|
||||
break;
|
||||
}
|
||||
|
||||
int sz = sizeof(char) * (len - count);
|
||||
char* classname = (char*)malloc(sz);
|
||||
snprintf(classname, sz, "%s", filename); // legitimately needs to be snprintf
|
||||
return classname;
|
||||
}
|
||||
|
||||
void* vmtranslateunit(void* input) {
|
||||
COMPILEUNIT* unit = (COMPILEUNIT*)input;
|
||||
|
||||
if(unit->compiled == NULL) {
|
||||
eprintf("Class '%s' is empty; file '%s'\n", unit->parsed->name, unit->file->name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char* classname = getclassname(unit->file->name);
|
||||
unit->vmtranslator = mkvmtranslator(classname, unit->compiled);
|
||||
unit->asmlns = translatevm(unit->vmtranslator);
|
||||
free(classname);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void waitthreads(pthread_t* threads, int amount) {
|
||||
void* status;
|
||||
int code;
|
||||
for(int i = 0; i < amount; i++) {
|
||||
code = pthread_join(threads[i], &status);
|
||||
if(code) {
|
||||
eprintf("Error while joining thread %i: %s\n", i, strerror(code));
|
||||
exit(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void actonunits(COMPILEUNIT* units, void*(*fun)(void*)) {
|
||||
pthread_t mythreads[_SC_THREAD_THREADS_MAX];
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
|
||||
COMPILEUNIT* curr = units;
|
||||
|
||||
int i;
|
||||
int code;
|
||||
do {
|
||||
i = 0;
|
||||
while(curr != NULL && i < _SC_THREAD_THREADS_MAX) {
|
||||
code = pthread_create(&mythreads[i], &attr, fun, curr);
|
||||
|
||||
if(code) {
|
||||
eprintf("Error while creating thread %i: %s\n", i, strerror(code));
|
||||
exit(code);
|
||||
}
|
||||
|
||||
curr = curr->next;
|
||||
i++;
|
||||
}
|
||||
waitthreads(mythreads, i);
|
||||
} while(i == _SC_THREAD_THREADS_MAX);
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
void freeunit(COMPILEUNIT* u) {
|
||||
freeparser(u->parser);
|
||||
freelnblk(u->compiled);
|
||||
freestrlist(u->asmlns);
|
||||
freevmtranslator(u->vmtranslator);
|
||||
free(u);
|
||||
}
|
29
misc/threads.h
Normal file
29
misc/threads.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
#include <pthread.h>
|
||||
#include "parser.h"
|
||||
#include "compiler.h"
|
||||
#include "io.h"
|
||||
#include "vm-translator.h"
|
||||
|
||||
/* threads
|
||||
* Tools for dealing with the compiling pipeline in a parallel way */
|
||||
|
||||
typedef struct unit {
|
||||
FILELIST* file;
|
||||
PARSER* parser;
|
||||
CLASS* parsed;
|
||||
COMPILER* compiler;
|
||||
STRINGLIST* asmlns;
|
||||
LINEBLOCK* compiled;
|
||||
VMTRANSLATOR* vmtranslator;
|
||||
struct unit* next;
|
||||
} COMPILEUNIT;
|
||||
|
||||
void* parseunit(void* input);
|
||||
void* compileunit(void* input);
|
||||
void* vmtranslateunit(void* input);
|
||||
void waitthreads(pthread_t* threads, int amount);
|
||||
void actonunits(COMPILEUNIT* units, void*(*fun)(void*));
|
||||
void freeunit(COMPILEUNIT* u);
|
||||
#endif
|
89
misc/util.c
Normal file
89
misc/util.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "util.h"
|
||||
|
||||
char* heapstr(const char* str, int len) {
|
||||
int size = sizeof(char) * (len + 1);
|
||||
char* outstr = (char*)malloc(size);
|
||||
strcpy(outstr, str);
|
||||
return outstr;
|
||||
}
|
||||
|
||||
char* ezheapstr(const char* str) {
|
||||
return heapstr(str, strlen(str));
|
||||
}
|
||||
|
||||
void* copy(void* v, int size) {
|
||||
void* copy = malloc(size);
|
||||
memcpy(copy, v, size);
|
||||
return copy;
|
||||
}
|
||||
|
||||
int countplaces(int n) {
|
||||
int places = 1;
|
||||
int divisor = 1;
|
||||
if(n < 0) {
|
||||
n = -n;
|
||||
places++;
|
||||
}
|
||||
while(n / divisor >= 10) {
|
||||
places++;
|
||||
divisor *= 10;
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
char* itoa(int i) {
|
||||
int size = sizeof(char)*(countplaces(i)+1);
|
||||
char* a = (char*)malloc(size);
|
||||
sprintf(a, "%i", i);
|
||||
return a;
|
||||
}
|
||||
|
||||
char* dotlabel(char* n1, char* n2) {
|
||||
int sz = (strlen(n1) + strlen(n2) + 2) * sizeof(char);
|
||||
char* result = (char*)malloc(sz);
|
||||
sprintf(result, "%s.%s", n1, n2);
|
||||
return result;
|
||||
}
|
||||
|
||||
STRINGLIST* onestr(const char* str) {
|
||||
STRINGLIST* strlist = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
strlist->content = ezheapstr(str);
|
||||
strlist->next = NULL;
|
||||
return strlist;
|
||||
}
|
||||
|
||||
STRINGLIST* initstrlist(const char** strs, int count) {
|
||||
STRINGLIST* strlist = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
STRINGLIST* curr = strlist;
|
||||
for(int i = 0; i < count-1; i++) {
|
||||
curr->content = ezheapstr(strs[i]);
|
||||
curr->next = (STRINGLIST*)malloc(sizeof(STRINGLIST));
|
||||
curr = curr->next;
|
||||
}
|
||||
curr->content = ezheapstr(strs[count-1]);
|
||||
curr->next = NULL;
|
||||
return strlist;
|
||||
}
|
||||
|
||||
void printstrlist(STRINGLIST* strlist, FILE* stream) {
|
||||
while(strlist != NULL) {
|
||||
fprintf(stream, "%s\n", strlist->content);
|
||||
strlist = strlist->next;
|
||||
}
|
||||
}
|
||||
|
||||
void freestrlist(STRINGLIST* strlist) {
|
||||
STRINGLIST* next = strlist->next;
|
||||
free(strlist);
|
||||
if(next != NULL)
|
||||
freestrlist(next);
|
||||
}
|
||||
|
||||
bool existsinarray(STRINGARRAY* arr, const char* item) {
|
||||
for(int i = 0; i < arr->size; i++)
|
||||
if(!strcmp(arr->items[i], item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
38
misc/util.h
Normal file
38
misc/util.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* util
|
||||
* Random utilities. */
|
||||
|
||||
// Macros
|
||||
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
|
||||
#define count(array, type) ((sizeof(array)) / (sizeof(type)))
|
||||
#define strcount(array) count(array, char*)
|
||||
#define mkstrlist(name, array) STRINGARRAY name = { .items = array, .size = strcount(array) }
|
||||
|
||||
typedef struct stringlist {
|
||||
char* content;
|
||||
struct stringlist* next;
|
||||
} STRINGLIST;
|
||||
|
||||
typedef struct {
|
||||
const char** items;
|
||||
const int size;
|
||||
} STRINGARRAY;
|
||||
|
||||
char* heapstr(const char* str, int len);
|
||||
char* ezheapstr(const char* str);
|
||||
int countplaces(int n);
|
||||
char* itoa(int i);
|
||||
void* copy(void* v, int size);
|
||||
char* dotlabel(char* n1, char* n2);
|
||||
|
||||
STRINGLIST* onestr(const char* str);
|
||||
STRINGLIST* initstrlist(const char** strs, int count);
|
||||
void printstrlist(STRINGLIST* strlist, FILE* stream);
|
||||
void freestrlist(STRINGLIST* strlist);
|
||||
|
||||
bool existsinarray(STRINGARRAY* arr, const char* item);
|
||||
#endif
|
Reference in New Issue
Block a user