https://github.com/akkartik/mu/blob/master/087file.cc
  1 //: Interacting with the file system.
  2 //:   '$open-file-for-reading' returns a FILE* as a number (ugh)
  3 //:   '$read-from-file' accepts a number, interprets it as a FILE* (double ugh) and reads a character from it
  4 //: Similarly for writing files.
  5 //: These interfaces are ugly and tied to the current (Linux) host Mu happens
  6 //: to be implemented atop. Later layers will wrap them with better, more
  7 //: testable interfaces.
  8 //:
  9 //: Clearly we don't care about performance or any of that so far.
 10 //: todo: reading/writing binary files
 11 
 12 :(before "End Primitive Recipe Declarations")
 13 _OPEN_FILE_FOR_READING,
 14 :(before "End Primitive Recipe Numbers")
 15 put(Recipe_ordinal, "$open-file-for-reading", _OPEN_FILE_FOR_READING);
 16 :(before "End Primitive Recipe Checks")
 17 
#include<assert.h>
#include<cstdlib>
#include<dirent.h>
#include<vector>
using std::vector;
#include<string>
using std::string;
#include<iostream>
using std::cout;

int main(int argc, const char* argv[]) {
  assert(argc == 3);
  assert(string(argv[1]) == "--until");
  string last_file(argv[2]);

  dirent** files;
  int num_files = scandir(".", &files, NULL, alphasort);
  for (int i = 0; i < num_files; ++i) {
    string curr_file = files[i]->d_name;
    if (!isdigit(curr_file.at(0))) continue;
    if (!last_file.empty() && curr_file > last_file) break;
    cout << curr_file << '\n';
  }
  // don't bother freeing files
  return 0;
}
should be a string, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); 59 break; 60 } 61 if (SIZE(inst.products) != 1) { 62 raise << maybe(get(Recipe, r).name) << "'$open-file-for-writing' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); 63 break; 64 } 65 if (!is_mu_number(inst.products.at(0))) { 66 raise << maybe(get(Recipe, r).name) << "first product of '$open-file-for-writing' should be a number (file handle), but got '" << to_string(inst.products.at(0)) << "'\n" << end(); 67 break; 68 } 69 break; 70 } 71 :(before "End Primitive Recipe Implementations") 72 case _OPEN_FILE_FOR_WRITING: { 73 string filename = read_mu_text(ingredients.at(0).at(/*skip alloc id*/1)); 74 assert(sizeof(long long int) >= sizeof(FILE*)); 75 long long int result = reinterpret_cast<long long int>(fopen(filename.c_str(), "w")); 76 products.resize(1); 77 products.at(0).push_back(static_cast<double>(result)); 78 break; 79 } 80 81 :(before "End Primitive Recipe Declarations") 82 _READ_FROM_FILE, 83 :(before "End Primitive Recipe Numbers") 84 put(Recipe_ordinal, "$read-from-file", _READ_FROM_FILE); 85 :(before "End Primitive Recipe Checks") 86 case _READ_FROM_FILE: { 87 if (SIZE(inst.ingredients) != 1) { 88 raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); 89 break; 90 } 91 if (!is_mu_number(inst.ingredients.at(0))) { 92 raise << maybe(get(Recipe, r).name) << "first ingredient of '$read-from-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); 93 break; 94 } 95 if (SIZE(inst.products) != 2) { 96 raise << maybe(get(Recipe, r).name) << "'$read-from-file' requires exactly two products, but got '" << to_original_string(inst) << "'\n" << end(); 97 break; 98 } 99 if (!is_mu_character(inst.products.at(0))) { 100 raise << maybe(get(Recipe, r).name) << "first product of '$read-from-file' should be a character, but got '" << to_string(inst.products.at(0)) << "'\n" << end(); 101 break; 102 } 103 if (!is_mu_boolean(inst.products.at(1))) { 104 raise << maybe(get(Recipe, r).name) << "second product of '$read-from-file' should be a boolean, but got '" << to_string(inst.products.at(1)) << "'\n" << end(); 105 break; 106 } 107 break; 108 } 109 :(before "End Primitive Recipe Implementations") 110 case _READ_FROM_FILE: { 111 long long int x = static_cast<long long int>(ingredients.at(0).at(0)); 112 FILE* f = reinterpret_cast<FILE*>(x); 113 if (f == NULL) { 114 raise << maybe(current_recipe_name()) << "can't read from null file in '" << to_string(current_instruction()) << "'\n" << end(); 115 break; 116 } 117 products.resize(2); 118 if (feof(f)) { 119 products.at(0).push_back(0); 120 products.at(1).push_back(1); // eof 121 break; 122 } 123 if (ferror(f)) { 124 raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(current_instruction()) << "'\n" << end(); 125 break; 126 } 127 char c = getc(f); // todo: unicode 128 if (c == EOF) { 129 products.at(0).push_back(0); 130 products.at(1).push_back(1); // eof 131 break; 132 } 133 if (ferror(f)) { 134 raise << maybe(current_recipe_name()) << "couldn't read from file in '" << to_string(current_instruction()) << "'\n" << end(); 135 raise << " errno: " << errno << '\n' << end(); 136 break; 137 } 138 products.at(0).push_back(c); 139 products.at(1).push_back(0); // not eof 140 break; 141 } 142 :(before "End Includes") 143 #include <errno.h> 144 145 :(before "End Primitive Recipe Declarations") 146 _WRITE_TO_FILE, 147 :(before "End Primitive Recipe Numbers") 148 put(Recipe_ordinal, "$write-to-file", _WRITE_TO_FILE); 149 :(before "End Primitive Recipe Checks") 150 case _WRITE_TO_FILE: { 151 if (SIZE(inst.ingredients) != 2) { 152 raise << maybe(get(Recipe, r).name) << "'$write-to-file' requires exactly two ingredients, but got '" << to_original_string(inst) << "'\n" << end(); 153 break; 154 } 155 if (!is_mu_number(inst.ingredients.at(0))) { 156 raise << maybe(get(Recipe, r).name) << "first ingredient of '$write-to-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); 157 break; 158 } 159 if (!is_mu_character(inst.ingredients.at(1))) { 160 raise << maybe(get(Recipe, r).name) << "second ingredient of '$write-to-file' should be a character, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); 161 break; 162 } 163 if (!inst.products.empty()) { 164 raise << maybe(get(Recipe, r).name) << "'$write-to-file' writes to no products, but got '" << to_original_string(inst) << "'\n" << end(); 165 break; 166 } 167 break; 168 } 169 :(before "End Primitive Recipe Implementations") 170 case _WRITE_TO_FILE: { 171 long long int x = static_cast<long long int>(ingredients.at(0).at(0)); 172 FILE* f = reinterpret_cast<FILE*>(x); 173 if (f == NULL) { 174 raise << maybe(current_recipe_name()) << "can't write to null file in '" << to_string(current_instruction()) << "'\n" << end(); 175 break; 176 } 177 if (feof(f)) break; 178 if (ferror(f)) { 179 raise << maybe(current_recipe_name()) << "file in invalid state in '" << to_string(current_instruction()) << "'\n" << end(); 180 break; 181 } 182 long long int y = static_cast<long long int>(ingredients.at(1).at(0)); 183 char c = static_cast<char>(y); 184 putc(c, f); // todo: unicode 185 if (ferror(f)) { 186 raise << maybe(current_recipe_name()) << "couldn't write to file in '" << to_string(current_instruction()) << "'\n" << end(); 187 raise << " errno: " << errno << '\n' << end(); 188 break; 189 } 190 break; 191 } 192 193 :(before "End Primitive Recipe Declarations") 194 _CLOSE_FILE, 195 :(before "End Primitive Recipe Numbers") 196 put(Recipe_ordinal, "$close-file", _CLOSE_FILE); 197 :(before "End Primitive Recipe Checks") 198 case _CLOSE_FILE: { 199 if (SIZE(inst.ingredients) != 1) { 200 raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one ingredient, but got '" << to_original_string(inst) << "'\n" << end(); 201 break; 202 } 203 if (!is_mu_number(inst.ingredients.at(0))) { 204 raise << maybe(get(Recipe, r).name) << "first ingredient of '$close-file' should be a number, but got '" << to_string(inst.ingredients.at(0)) << "'\n" << end(); 205 break; 206 } 207 if (SIZE(inst.products) != 1) { 208 raise << maybe(get(Recipe, r).name) << "'$close-file' requires exactly one product, but got '" << to_original_string(inst) << "'\n" << end(); 209 break; 210 } 211 if (inst.products.at(0).name != inst.ingredients.at(0).name) { 212 raise << maybe(get(Recipe, r).name) << "'$close-file' requires its product to be the same as its ingredient, but got '" << to_original_string(inst) << "'\n" << end(); 213 break; 214 } 215 break; 216 } 217 :(before "End Primitive Recipe Implementations") 218 case _CLOSE_FILE: { 219 long long int x = static_cast<long long int>(ingredients.at(0).at(0)); 220 FILE* f = reinterpret_cast<FILE*>(x); 221 fclose(f); 222 products.resize(1); 223 products.at(0).push_back(0); // todo: ensure that caller always resets the ingredient 224 break; 225 }