/* * save_restore.c * * Emulates the MudOS save_variable and restore_variable efunctions. * * 8 January 1997 Begun Greg Lewis * # Wrote and tested initial version. restore_variable needs to do * error checking though (i.e., it works if the string it gets is * right, but crashes and burns otherwise ;). * * 10 January 1997 Version 1.0 Greg Lewis * # Added error checking to restore_variable, it returns RESTORE_ERROR * (#define'd in save_restore.h) upon errors in the restore process. * # Everything seems to test ok, not that I've been all that thorough. * * TBD * # Prolly needs some more commenting (doesn't most code?) */ /* Includes */ #include <type.h> #include <m_save_restore.h> /* Prototypes */ private static varargs string grab_value_string(string saved_ma, string separator); private static int find_string_end(string string_bit); private static int find_arraymap_end(string array_bit, int type); /* * NAME: save_variable * DESCRIPTION: Given any value it produces a string which represents the * value in the MudOS save/restore format. */ string save_variable(mixed var) { string result; int i, s; mixed *keys, *values; if (!var) { return "0"; } switch (typeof(var)) { case T_INT : case T_FLOAT : result = ""+var; break; case T_STRING : result = implode(explode(var, "\\"), "\\\\"); result = implode(explode(result, "\""), "\\\""); result = "\""+result+"\""; break; case T_OBJECT : result = object_name(var); break; case T_ARRAY : result = "({"; for (i = 0, s = sizeof(var); i < s; i++) { result += save_variable(var[i]) + ","; } result += "})"; break; case T_MAPPING : keys = map_indices(var); values = map_values(var); result = "(["; for (i = 0, s = map_sizeof(var); i < s; i++) { result += save_variable(keys[i]) + ":" + save_variable(values[i]) + ","; } result += "])"; break; default : result = 0; break; } return result; } /* * NAME: restore_variable * DESCRIPTION: Given a string in the format returned by save_variable, * restore_variable returns a copy of what was originally saved, * or RESTORE_ERROR in case of an error. */ mixed restore_variable(string saved_var) { int s; mixed *a, *tmp; mapping m; string elt, elt2; object ob; int number; float real; if (!saved_var || !(s = strlen(saved_var))) { return RESTORE_ERROR; } switch (saved_var[0]) { case '"' : if (s < 2 || find_string_end(saved_var[1 .. s-1]) != s-2) { return RESTORE_ERROR; } elt = implode(explode(saved_var[1 .. s-2], "\\\\"), "\\"); elt = implode(explode(elt, "\\\""), "\""); return elt; break; case '/' : ob = find_object(saved_var); return (ob) ? ob : RESTORE_ERROR; break; case '(' : if (s < 4) { return RESTORE_ERROR; } if (saved_var[1] == '{') { if (saved_var == "({})") { return ({ }); } if (find_arraymap_end(saved_var[2 .. s-1], T_ARRAY) != s-3) { return RESTORE_ERROR; } number = 2; a = ({ }); while (number < s-2) { elt = grab_value_string(saved_var[number .. s-1]); if (!elt) { return RESTORE_ERROR; } number += strlen(elt)+1; a += ({ restore_variable(elt) }); } return a; } else if (saved_var[1] == '[') { if (find_arraymap_end(saved_var[2 .. s-1], T_MAPPING) != s-3) { return RESTORE_ERROR; } if (saved_var == "([])") { return ([ ]); } number = 2; m = ([ ]); while (number < s-2) { elt = grab_value_string(saved_var[number .. s-1], ":"); if (!elt) { return RESTORE_ERROR; } number += strlen(elt)+1; elt2 = grab_value_string(saved_var[number .. s-1]); if (!elt2) { return RESTORE_ERROR; } number += strlen(elt2)+1; m[restore_variable(elt)] = restore_variable(elt2); } return m; } return RESTORE_ERROR; break; default : /* array of one byte strings */ a = explode(saved_var, ""); /* Check valid chars for numbers */ tmp = a - ({ "0","1","2","3","4","5","6","7","8","9","-","." }); if (sizeof(tmp)) { /* Should also check if there is a - its at the start * and there is something after it */ return RESTORE_ERROR; } if (!sscanf(saved_var, "%*s.%*s")) { if (!sscanf(saved_var, "%d", number)) { return RESTORE_ERROR; } return number; } else { if (!sscanf(saved_var, "%f", real)) { return RESTORE_ERROR; } return real; } break; } } /* * NAME: grab_string_value * DESCRIPTION: Grabs the next element in an array or the next key/value in * a mapping and returns the string representing it in * the save_variable format. The array or mapping is input in * in this same format. 0 is returned on error and separator * defaults to ",". See restore_variable for an example of using * this. */ private static varargs string grab_value_string(string saved_ma, string separator) { int pos; if (!saved_ma) { return 0; } if (!separator) { separator = ","; } switch (saved_ma[0]) { case '"' : pos = find_string_end(saved_ma[1 .. ]); if (pos == -1) { return 0; } return saved_ma[0 .. pos+1]; break; case '(' : if (saved_ma[1] == '{') { pos = find_arraymap_end(saved_ma[2 .. ], T_ARRAY); if (pos == -1) { return 0; } return saved_ma[0 .. pos+2]; } if (saved_ma[1] == '[') { pos = find_arraymap_end(saved_ma[2 .. ], T_MAPPING); if (pos == -1) { return 0; } return saved_ma[0 .. pos+2]; } return 0; default : /* Should cover objects, ints and floats */ return explode(saved_ma, separator)[0]; } } /* * NAME: find_string_end * DESCRIPTION: Given a string in the save_variable format (except for the * leading ") returns the position at which the matching " occurs. * Returns -1 on error. */ private static int find_string_end(string string_bit) { int last_char; int pos; if (string_bit[0] == '"') { return 0; } last_char = string_bit[0]; pos = 1; while (pos < strlen(string_bit)) { if (string_bit[pos] == '"' && last_char != '\\') { return pos; } last_char = string_bit[pos++]; } return -1; /* Error */ } /* * NAME: find_array_map_end * DESCRIPTION: Given an array or mapping in the save_variable format (except * for the leading ({ or ([) returns the position at which the * end character of the matching }) or ]) occurs. * Returns -1 on error. */ private static int find_arraymap_end(string array_bit, int type) { int pos; int last_char; int in_array, in_mapping, in_string; /* We need two chars at least for an array or map ending */ if (!array_bit || strlen(array_bit) < 2) { return -1; } in_array = (type == T_ARRAY) ? 1 : 0; in_mapping = (type == T_MAPPING) ? 1 : 0; if (!in_array && !in_mapping) { return -1; } in_string = 0; last_char = (type == T_ARRAY) ? '{' : '['; pos = 0; while (pos < strlen(array_bit)) { if (!in_string) { if (array_bit[pos] == '"') { in_string = 1; continue; } if (array_bit[pos] == ')' && last_char == '}') { in_array--; } else if (array_bit[pos] == ')' && last_char == ']') { in_mapping--; } else if (array_bit[pos] == '{' && last_char == '(') { in_array++; } else if (array_bit[pos] == '[' && last_char == '(') { in_mapping++; } } else { if (array_bit[pos] == '"' && last_char != '\\') { in_string = 0; } } if (!in_array && !in_mapping && !in_string) { return pos; } last_char = array_bit[pos++]; } return -1; /* Error */ }