7e4c942016-04-29Chris Angelico //! parse_line.pike
53a9021998-10-14Niels Möller 
a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
53a9021998-10-14Niels Möller class partial_literal { int length; void create(int l) { length = l; } } string buffer; void create(string line) { buffer = line; } void skip_whitespace() { sscanf(buffer, "%*[ \t]%s", buffer); }
4d026b1998-10-26Niels Möller #if 0
e992931998-10-21Niels Möller int eolp() { skip_whitespace();
ead9722003-01-20Martin Nilsson  return !sizeof(buffer);
e992931998-10-21Niels Möller }
4d026b1998-10-26Niels Möller #endif
e992931998-10-21Niels Möller 
7e4c942016-04-29Chris Angelico // Returns -1 on error. All valid numbers are non-negative.
53a9021998-10-14Niels Möller int get_number() { skip_whitespace();
3524712015-05-26Martin Nilsson 
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
53a9021998-10-14Niels Möller  return -1; int i;
ead9722003-01-20Martin Nilsson  for(i = 0; i<sizeof(buffer); i++)
53a9021998-10-14Niels Möller  if ( (buffer[i] < '0') || ('9' < buffer[i]) ) break;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  // Disallow too large numbers if (!i || (i > 9) ) return -1;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  int res = array_sscanf(buffer[..i-1], "%d")[0]; buffer = buffer[i..]; return res; } string get_atom(int|void with_options) { string atom; werror("get_atom: buffer = '%s'\n", buffer);
5e5d111999-02-02Henrik Grubbström (Grubba) 
53a9021998-10-14Niels Möller  sscanf(buffer, (with_options
d5cd421999-02-13Henrik Grubbström (Grubba)  ? "%*[ \t]%[^][(){ \0-\037\177%\"]%s" : "%*[ \t]%[^(){ \0-\037\177%\"]%s"),
53a9021998-10-14Niels Möller  atom, buffer);
5e5d111999-02-02Henrik Grubbström (Grubba) 
88ad2d2013-05-19Martin Nilsson  werror("=> atom: %O\n", atom);
ead9722003-01-20Martin Nilsson  return sizeof(atom) && atom;
53a9021998-10-14Niels Möller } string|object get_string() { werror("get_string: buffer = '%s'\n", buffer); skip_whitespace();
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
53a9021998-10-14Niels Möller  return 0;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  switch(buffer[0]) { case '"': { int i = 0; while(1) { i = search(buffer, "\"", i+1); if (i<0) return 0; if (buffer[i-1] != '\\') { string res = replace(buffer[1..i-1], ({ "\\\"", "\\\\" }), ({ "\"", "\\" }) ); buffer = buffer[i+1..]; return res; } } } case '{': {
ead9722003-01-20Martin Nilsson  if (buffer[sizeof(buffer)-1..] != "}")
53a9021998-10-14Niels Möller  return 0;
8a531a2006-11-04Martin Nilsson  string n = buffer[1..<1];
53a9021998-10-14Niels Möller  buffer = ""; if ( (sizeof(values(n) - values("0123456789"))) || (sizeof(n) > 9) ) return 0; if (!sizeof(n)) return 0; return partial_literal(array_sscanf(n, "%d")[0]); } } } string|object get_astring() { werror("get_astring: buffer = '%s'\n", buffer);
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  skip_whitespace();
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
53a9021998-10-14Niels Möller  return 0; switch(buffer[0]) { case '"': case '{': return get_string(); default: return get_atom(); } }
3524712015-05-26Martin Nilsson 
7e4c942016-04-29Chris Angelico //! Returns a set object.
53a9021998-10-14Niels Möller object get_set() { string atom = get_atom(); if (!atom) return 0;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  return .types.imap_set()->init(atom); }
7e4c942016-04-29Chris Angelico //! Parses an object that can be a string, an atom (possibly with //! options in brackets) or a list. //! //! eol can be 0, meaning no end of line or list expected, //! a positive int, meaning a character (e.g. ')' or ']' that terminates the list, //! or -1, meaning that the list terminates at end of line.
4d026b1998-10-26Niels Möller mapping get_token(int eol, int accept_options)
c6b66a1998-10-16Niels Möller { skip_whitespace();
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
4d026b1998-10-26Niels Möller  return (eol == -1) && ([ "type" : "eol", "eol" : 1 ]);
3524712015-05-26Martin Nilsson 
c6b66a1998-10-16Niels Möller  if (eol && (buffer[0] == eol)) { buffer = buffer[1..]; return ([ "type" : "eol", "eol" : 1 ]); } switch(buffer[0]) { case '(': buffer = buffer[1..]; return ([ "type" : "list", "list" : 1 ]); case '"': { string s = get_string(); return s && ([ "type" : "string", "string" : s ]); }
b119962000-01-04Henrik Grubbström (Grubba)  case '{': {
c6b66a1998-10-16Niels Möller  object s = get_string();
9406111998-10-16Niels Möller  return s && ([ "type" : "literal", "length" : s->length ]);
c6b66a1998-10-16Niels Möller  } default: {
a1b1301999-03-22Henrik Grubbström (Grubba)  if (accept_options) { return get_atom_options(2); } else { string atom = get_atom(accept_options);
c6b66a1998-10-16Niels Möller  return ([ "type" : "atom", "atom" : atom ]);
a1b1301999-03-22Henrik Grubbström (Grubba)  }
c6b66a1998-10-16Niels Möller  } } }
7e4c942016-04-29Chris Angelico //! Reads a <start.size> suffix
c6b66a1998-10-16Niels Möller mapping get_range(mapping atom) {
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer) || (buffer[0] != '<'))
c6b66a1998-10-16Niels Möller  return atom; buffer = buffer[1..]; int start = get_number();
ead9722003-01-20Martin Nilsson  if ((start < 0) || !sizeof(buffer) || (buffer[0] != '.'))
c6b66a1998-10-16Niels Möller  return 0; buffer = buffer[1..];
3524712015-05-26Martin Nilsson 
c6b66a1998-10-16Niels Möller  int size = get_number();
ead9722003-01-20Martin Nilsson  if ((size <= 0) || !sizeof(buffer) || (buffer[0] != '>'))
c6b66a1998-10-16Niels Möller  return 0;
3524712015-05-26Martin Nilsson 
c6b66a1998-10-16Niels Möller  buffer = buffer[1..]; atom->range = ({ start, size }); return atom; }
7e4c942016-04-29Chris Angelico //! Get a list of atoms. //! Primarily intended for use by STORE for the flags argument.
cf70fa1999-02-17Henrik Grubbström (Grubba) array(string) get_flag_list() { skip_whitespace();
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer) || (buffer[0] != '(') )
cf70fa1999-02-17Henrik Grubbström (Grubba)  return 0; buffer = buffer[1..];
a96b3d1999-02-18Henrik Grubbström (Grubba)  array(string) res = ({});
cf70fa1999-02-17Henrik Grubbström (Grubba)  skip_whitespace(); string atom; while (atom = get_atom()) { res += ({ atom }); skip_whitespace(); } skip_whitespace(); // This one shouldn't be needed, but...
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
d0e1571999-02-17Henrik Grubbström (Grubba)  return 0;
cf70fa1999-02-17Henrik Grubbström (Grubba)  if (buffer[0] = ')') { buffer = buffer[1..]; return res; } return 0; }
7e4c942016-04-29Chris Angelico //! Parses an object that (recursivly) can contain atoms (possible //! with options in brackets) or lists. Note that strings are not //! accepted, as it is a little difficult to wait for the //! continuation of the request. //! //! FIXME: This function is used to read fetch commands. This breaks //! rfc-2060 compliance, as the names of headers can be represented //! as string literals.
53a9021998-10-14Niels Möller mapping get_simple_list(int max_depth) { skip_whitespace();
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
53a9021998-10-14Niels Möller  return 0;
ee55471999-03-22Henrik Grubbström (Grubba)  if (buffer[0] == '(') {
53a9021998-10-14Niels Möller  /* Recurse */
ee55471999-03-22Henrik Grubbström (Grubba)  if (max_depth > 0) {
53a9021998-10-14Niels Möller  array a = do_parse_simple_list(max_depth - 1, ')'); return a && ([ "type": "list", "list": a ]);
ee55471999-03-22Henrik Grubbström (Grubba)  } else { werror("get_simple_list(): Too deep recursion.\n");
53a9021998-10-14Niels Möller  return 0;
ee55471999-03-22Henrik Grubbström (Grubba)  }
53a9021998-10-14Niels Möller  }
e160bb1999-03-22Henrik Grubbström (Grubba)  return 0; // return get_atom_options(max_depth-1);
53a9021998-10-14Niels Möller } array do_parse_simple_list(int max_depth, int terminator) { array a = ({ });
ee55471999-03-22Henrik Grubbström (Grubba) 
88ad2d2013-05-19Martin Nilsson  werror("do_parse_simple_list(%d, '%c')\n", max_depth, terminator);
3524712015-05-26Martin Nilsson 
c270a81999-03-22Henrik Grubbström (Grubba)  buffer = buffer[1..];
53a9021998-10-14Niels Möller  while(1) { skip_whitespace();
3524712015-05-26Martin Nilsson 
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer))
53a9021998-10-14Niels Möller  return 0;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  if (buffer[0] == terminator) { buffer = buffer[1..]; return a; }
c270a81999-03-22Henrik Grubbström (Grubba) 
ae99701999-03-22Henrik Grubbström (Grubba)  mapping atom = get_atom_options(max_depth-1); if (!atom) { return a; } a += ({ atom });
c270a81999-03-22Henrik Grubbström (Grubba) 
3142621999-03-22Henrik Grubbström (Grubba)  mapping m = get_simple_list(max_depth); if (!m) {
88ad2d2013-05-19Martin Nilsson  werror("get_simple_list(%d) failed\n", max_depth);
c270a81999-03-22Henrik Grubbström (Grubba)  } else {
ca768b1999-03-22Henrik Grubbström (Grubba)  a += ({ m });
3142621999-03-22Henrik Grubbström (Grubba)  }
53a9021998-10-14Niels Möller  } }
7e4c942016-04-29Chris Angelico //! Reads an atom, optionally followd by a list enclosed in square //! brackets. Naturally, the atom itself cannot contain any brackets. //! //! Returns a mapping //! type : "atom", //! atom : name, //! raw : name[options] //! options : parsed options, //! range : ({ start, size })
53a9021998-10-14Niels Möller mapping get_atom_options(int max_depth) { string atom = get_atom(1); if (!atom) return 0; mapping res = ([ "type" : "atom", "atom" : atom ]);
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer) || (buffer[0] != '['))
53a9021998-10-14Niels Möller  { res->raw = atom; return res; }
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  /* Parse options */ string option_start = buffer;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  array options = do_parse_simple_list(max_depth - 1, ']');
8eb8d61999-03-22Henrik Grubbström (Grubba) 
a1b1301999-03-22Henrik Grubbström (Grubba)  if (!options) {
ee55471999-03-22Henrik Grubbström (Grubba)  werror("get_atom_options(): No options.\n");
21eae21999-03-22Henrik Grubbström (Grubba)  res->raw = atom + "[]";
a1b1301999-03-22Henrik Grubbström (Grubba)  res->options = ({ ([ "type":"atom", "atom":"" ]) }); return res; }
53a9021998-10-14Niels Möller  res->options = options;
e160bb1999-03-22Henrik Grubbström (Grubba)  res->raw = atom +
8a531a2006-11-04Martin Nilsson  option_start[..<sizeof(buffer)];
8eb8d61999-03-22Henrik Grubbström (Grubba) 
3524712015-05-26Martin Nilsson 
ead9722003-01-20Martin Nilsson  if (!sizeof(buffer) || (buffer[0] != '<'))
53a9021998-10-14Niels Möller  return res; /* Parse <start.size> suffix */ buffer = buffer[1..]; int start = get_number();
ead9722003-01-20Martin Nilsson  if ((start < 0) || !sizeof(buffer) || (buffer[0] != '.'))
53a9021998-10-14Niels Möller  return 0; buffer = buffer[1..];
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  int size = get_number();
ead9722003-01-20Martin Nilsson  if ((size < 0) || !sizeof(buffer) || (buffer[0] != '>'))
53a9021998-10-14Niels Möller  return 0;
3524712015-05-26Martin Nilsson 
53a9021998-10-14Niels Möller  buffer = buffer[1..]; res->range = ({ start, size }); return res; }