+static bool pkt_nextstr(char **pkt, char **str, size_t *rem)
+{
+ size_t len;
+
+ if (!isprint(**pkt) || !(len = strnlen(*pkt, *rem))) {
+ return false;
+ } else if (str) {
+ *str = *pkt;
+ }
+
+ *pkt += len + 1;
+
+ if (*rem > 1) {
+ *rem -= len + 1;
+ } else {
+ *rem = 0;
+ }
+
+ return true;
+}
+
+static bool pkt_nextopt(char **pkt, char **opt, char **val, size_t *rem)
+{
+ return pkt_nextstr(pkt, opt, rem) && pkt_nextstr(pkt, val, rem);
+}
+
+static char *pkt_optval(char* pkt, const char* name)
+{
+ size_t rem = 512;
+ char *opt, *val;
+ pkt += 2;
+
+ while (pkt_nextopt(&pkt, &opt, &val, &rem)) {
+ if (!strcasecmp(name, opt)) {
+ return val;
+ }
+ }
+
+ return NULL;
+}
+
+static size_t pkt_xrqlen(char *pkt)
+{
+ size_t rem = 512;
+
+ pkt += 2;
+ while (pkt_nextopt(&pkt, NULL, NULL, &rem)) {
+ ;
+ }
+
+ return 514 - rem;
+}
+
+static void pkt_mkwrq(char *pkt, const char *filename, unsigned blksize)
+{