#include "rbxs_dom.h" #include "rbxs_domnode.h" #include #include #include #include #include //*********************************************************************************** // GC //*********************************************************************************** void rbxs_dom_free(rbxs_dom *prbxs_dom) { if (prbxs_dom != NULL) { xmlFreeDoc(prbxs_dom->doc); xmlCleanupParser(); free(prbxs_dom); } } void rbxs_dom_mark(rbxs_dom *prbxs_dom) { } //*********************************************************************************** // Methods //*********************************************************************************** VALUE rbxs_dom_to_s(VALUE self) { rbxs_dom *prbxs_dom; xmlChar *result; VALUE ret; int len; Data_Get_Struct(self, rbxs_dom, prbxs_dom); if (prbxs_dom->doc == NULL) { return(Qnil); } else if (prbxs_dom->doc->encoding != NULL) { xmlDocDumpFormatMemoryEnc(prbxs_dom->doc, &result, &len, prbxs_dom->doc->encoding, 1); } else { xmlDocDumpFormatMemory(prbxs_dom->doc, &result, &len, 1); } ret = rb_str_new2(result); xmlFree(result); return(ret); } VALUE rbxs_dom_root(VALUE self) { rbxs_dom *prbxs_dom; xmlNodePtr root; Data_Get_Struct(self, rbxs_dom, prbxs_dom); root = xmlDocGetRootElement(prbxs_dom->doc); if (root) return(rbxs_domnode_new(cSimpleDomNode, self, root)); else return(Qnil); } VALUE rbxs_dom_save_as(VALUE self, VALUE file) { Check_Type(file, T_FILE); rb_io_write(file,rbxs_dom_to_s(self)); return(Qtrue); } //*********************************************************************************** // Constructors //*********************************************************************************** VALUE rbxs_dom_string(VALUE class, VALUE string) { rbxs_dom *prbxs_dom; xmlParserCtxtPtr ctxt; Check_Type(string, T_STRING); prbxs_dom = (rbxs_dom *)malloc(sizeof(rbxs_dom)); if (prbxs_dom == NULL) rb_raise(rb_eNoMemError, "No memory left for XML::Simple::Dom struct"); xmlInitParser(); ctxt = xmlCreateMemoryParserCtxt(STR2CSTR(string),strlen(STR2CSTR(string))); if (ctxt == NULL) rb_sys_fail(STR2CSTR(string)); if (xmlParseDocument(ctxt) == -1) { xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); rb_raise(rb_eRuntimeError, "Document didn't parse"); } if (!ctxt->wellFormed) { xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); ctxt = NULL; rb_raise(rb_eRuntimeError, "Document is not wellformed"); } prbxs_dom->type = RBXS_DOM_TYPE_STRING; prbxs_dom->lock = 0; prbxs_dom->doc = ctxt->myDoc; xmlFreeParserCtxt(ctxt); return(Data_Wrap_Struct(class, rbxs_dom_mark, rbxs_dom_free, prbxs_dom)); } VALUE rbxs_dom_open(int argc, VALUE *argv, VALUE class) { rbxs_dom *prbxs_dom; xmlParserCtxtPtr ctxt; char *root, *lockfile; int lock, status; // this keeps prevents libxml from freezing, dunno why, but I will // keep it for now. I'd be very anxious to have an explanation for this. // I assume that there is some internal limit in libxml // call replace.rb without this line to see the freeze rb_gc_start(); ctxt = NULL; lockfile = NULL; Check_Type(argv[0], T_STRING); prbxs_dom = (rbxs_dom *)malloc(sizeof(rbxs_dom)); if (prbxs_dom == NULL) rb_raise(rb_eNoMemError, "No memory left for XML::Simple::Dom struct"); root = NULL; lock = 7; switch (argc) { case 3: if ((TYPE(argv[2]) == T_FIXNUM) && (TYPE(argv[1]) != T_STRING)) { Check_Type(argv[2], T_FIXNUM); lock = NUM2INT(argv[2]); root = STR2CSTR(argv[1]); } else if ((TYPE(argv[1]) == T_FIXNUM) && (TYPE(argv[2]) != T_STRING)) { Check_Type(argv[1], T_FIXNUM); lock = NUM2INT(argv[1]); root = STR2CSTR(argv[2]); } else rb_raise(rb_eArgError, "argument 2 and 3 have to be Hash and Integer"); break; case 2: if (TYPE(argv[1]) == T_STRING) root = STR2CSTR(argv[1]); else if (TYPE(argv[1]) == T_FIXNUM) { Check_Type(argv[2], T_FIXNUM); lock = NUM2INT(argv[2]); } else rb_raise(rb_eArgError, "argument 2 has to be a String or an Integer"); break; case 1: break; default: rb_raise(rb_eArgError, "wrong number of arguments (need 2 or 3)"); } if (root != NULL) { int fd; fd = open(STR2CSTR(argv[0]), O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR); if (fd < 0) { if (errno != EEXIST) rb_raise(rb_eRuntimeError, "Failed to create new file"); } else { char *wr; wr = (char *)calloc(strlen("\n") + strlen(root) + 1,sizeof(char)); strncat(wr,"\n",strlen("\n")); strncat(wr,root,strlen(root)); if (!(write(fd,wr,strlen(wr)) > 0)) rb_raise(rb_eRuntimeError, "Failed to write new file"); free(wr); } close(fd); } prbxs_dom->type = RBXS_DOM_TYPE_FILE; prbxs_dom->lock = lock; if (rb_block_given_p()) { struct stat buffer; lockfile = (char *)calloc(strlen(STR2CSTR(argv[0])) + 6,sizeof(char)); strncat(lockfile,STR2CSTR(argv[0]),strlen(STR2CSTR(argv[0]))); strncat(lockfile,".lock",5); if (prbxs_dom->lock > 0) { int fd; do { fd = open(lockfile, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR); if (fd < 0) { if (errno != EEXIST) { xmlFreeDoc(ctxt->myDoc); ctxt = NULL; free(lockfile); rb_raise(rb_eRuntimeError, "Failed to create lockfile"); } else { status = stat(lockfile, &buffer); if (buffer.st_mtime < time(NULL) - prbxs_dom->lock) unlink(lockfile); } } rb_thread_polling(); } while (fd < 0); close(fd); } } xmlInitParser(); ctxt = xmlCreateFileParserCtxt(STR2CSTR(argv[0])); if (ctxt == NULL) { if (rb_block_given_p()) { if (prbxs_dom->lock > 0) unlink(lockfile); free(lockfile); } rb_sys_fail(STR2CSTR(argv[0])); } if (xmlParseDocument(ctxt) == -1) { if (rb_block_given_p()) { if (prbxs_dom->lock > 0) unlink(lockfile); free(lockfile); } xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); rb_raise(rb_eRuntimeError, "Document didn't parse"); } if (!ctxt->wellFormed) { if (rb_block_given_p()) { if (prbxs_dom->lock > 0) unlink(lockfile); free(lockfile); } xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); ctxt = NULL; rb_raise(rb_eRuntimeError, "Document is not wellformed"); } prbxs_dom->doc = ctxt->myDoc; xmlFreeParserCtxt(ctxt); xmlCleanupParser(); // Hmmm, no problem to call it as this point if (rb_block_given_p()) { rb_yield(Data_Wrap_Struct(class, rbxs_dom_mark, rbxs_dom_free, prbxs_dom)); status = xmlSaveFormatFile(STR2CSTR(argv[0]), prbxs_dom->doc, 1); if (prbxs_dom->lock > 0) unlink(lockfile); free(lockfile); if (status == -1) rb_raise(rb_eRuntimeError, "Unable to write file back to disk"); return(Qnil); } return(Data_Wrap_Struct(class, rbxs_dom_mark, rbxs_dom_free, prbxs_dom)); } //*********************************************************************************** // Initialize module and class //*********************************************************************************** VALUE cSimpleDom; void init_rbxs_dom( void ) { cSimpleDom = rb_define_class_under( cSimple, "Dom", rb_cObject ); rb_define_method(cSimpleDom, "to_s", rbxs_dom_to_s, 0); rb_define_method(cSimpleDom, "inspect", rbxs_dom_to_s, 0); rb_define_method(cSimpleDom, "root", rbxs_dom_root, 0); rb_define_method(cSimpleDom, "save_as", rbxs_dom_save_as, 1); }