#include "__write.h" #include "Exception.h" #include "Operand.h" #include #include #include namespace jet { __write::__write(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { output = false; int mode = 0; int len; processContainer(container); if(!keywordDefined("file")) throw coreutils::Exception("write tag must have file defined."); if(!keywordDefined("expr") && keywordDefined("value") && hasContainer) throw coreutils::Exception("write tag cannot have both value and a container."); if(keywordDefined("expr") && !keywordDefined("value") && hasContainer) throw coreutils::Exception("write tag cannot have both expr and a container."); if(keywordDefined("expr") && keywordDefined("value") && !hasContainer) throw coreutils::Exception("write tag cannot have both expr and value."); if(!keywordDefined("expr") && !keywordDefined("value") && !hasContainer) throw coreutils::Exception("write tag must have a value, expr or a container."); if(!keywordDefined("mode")) throw coreutils::Exception("write tag must have a mode keyword."); // resolveKeyword("mode") already returns the resolved VALUE (e.g. // "overwrite") - re-indexing keywords[...] with that value treats // it as if it were a MAP KEY instead (auto-vivifying an empty // string, since no keyword is literally named "overwrite"/ // "append"), so this comparison was false for every real input // and threw "mode keyword must be..." unconditionally. // Same double-indirection bug already fixed elsewhere in this // codebase (__mysql.cpp's own constructor, __sql.cpp's sessionid // handling) - confirmed via a real repro building the jet-2.0 // htmleditor port's file-based storage. coreutils::MString modeValue = resolveKeyword("mode"); if(modeValue == "append") mode = O_APPEND; else if(modeValue == "overwrite") mode = O_TRUNC; else throw coreutils::Exception("mode keyword must be 'overwrite' or 'append'."); // mode alone (O_APPEND or O_TRUNC) has no access-mode bits at // all - open()'s access mode defaults to O_RDONLY (0) unless // O_WRONLY/O_RDWR is explicitly OR'd in, so the write() call below // was failing on every real invocation (EBADF - can't write a // read-only fd), and without O_CREAT this could never create a // NEW file either, only (theoretically) write an existing one. // Confirmed via a real repro: // silently produced a 0-byte or missing file every time. // Same double-indirection bug as "mode" above. int fd = open(resolveKeyword("file").c_str(), mode | O_WRONLY | O_CREAT, 0644); // TODO: Need to add AUTH flags. if(hasContainer && !evaluate) len = write(fd, container.getData(), container.getLength()); else if(hasContainer && evaluate) len = write(fd, out.getData(), out.getLength()); else if(!hasContainer && keywordDefined("value")) { // Same double-indirection bug as "mode"/"file" above: // variables[resolveKeyword("value")] used the resolved VALUE // itself as a lookup key into global.variables (a DIFFERENT map // entirely), not the literal value to write - looking up a // variable literally named e.g. "hello world" auto-vivifies an // empty entry, so this wrote 0 bytes for every real invocation. // Confirmed via a real repro: produced a genuinely empty file. coreutils::MString writeValue = resolveKeyword("value"); len = write(fd, writeValue.getData(), writeValue.getLength()); } else if(!hasContainer && keywordDefined("expr")) len = write(fd, keywords["expr"].getData(), keywords["expr"].getLength()); close(fd); } }