plptools
Loading...
Searching...
No Matches
ftp.cc
Go to the documentation of this file.
1/*
2 * This file is part of plptools.
3 *
4 * Copyright (C) 1999 Philip Proudman <philip.proudman@btinternet.com>
5 * Copyright (C) 1999-2002 Fritz Elfert <felfert@to.com>
6 * Copyright (C) 2006-2025 Reuben Thomas <rrt@sc3d.org>
7 * Copyright (C) 2026 Jason Morley <hello@jbmorley.co.uk>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * along with this program; if not, see <https://www.gnu.org/licenses/>.
21 *
22 */
23
24#include "config.h"
25
26#include <bufferarray.h>
27#include <bufferstore.h>
28#include <cstdint>
29#include <drive.h>
30#include <Enum.h>
31#include <memory>
32#include <pathutils.h>
33#include <plpintl.h>
34#include <rclip.h>
35#include <rfsv.h>
36#include <rpcs.h>
37#include <tcpsocket.h>
38
39#include <iostream>
40#include <string>
41#include <iomanip>
42
43#include <sys/types.h>
44#include <dirent.h>
45#include <ctype.h>
46#include <stdlib.h>
47#include <stdio.h>
48#include <time.h>
49#include <unistd.h>
50#include <sys/time.h>
51#include <sys/stat.h>
52#include <signal.h>
53#include <netdb.h>
54#include <fnmatch.h>
55
56#include "ignore-value.h"
57#include "string-buffer.h"
58#include "xalloc.h"
59#include "xvasprintf.h"
60
61#include "ftp.h"
62
63extern "C" {
64#include "yesno.h"
65
66#if defined(HAVE_READLINE_READLINE_H)
67# include <readline/readline.h>
68#elif defined(HAVE_READLINE_H)
69# include <readline.h>
70#else /* !defined(HAVE_READLINE_H) */
71extern char *readline ();
72#endif /* !defined(HAVE_READLINE_H) */
73#ifdef HAVE_READLINE_HISTORY
74# if defined(HAVE_READLINE_HISTORY_H)
75# include <readline/history.h>
76# elif defined(HAVE_HISTORY_H)
77# include <history.h>
78# endif /* !defined(HAVE_READLINE_HISTORY_H) */
79#endif /* !defined(HAVE_READLINE_HISTORY) */
80}
81
82using namespace std;
83
84static char *psionDir;
85static RFSV *comp_a;
86static int continueRunning;
87
88#define CLIPFILE "C:/System/Data/Clpboard.cbd"
89
90
92 localDir = getcwd(NULL, 0);
93 assert(localDir != NULL);
94}
95
98}
99
101 free(localDir);
102}
103
105 cout << _("Known FTP commands:") << endl << endl;
106 cout << " pwd" << endl;
107 cout << " ren <oldname> <newname>" << endl;
108 cout << " touch <psionfile>" << endl;
109 cout << " gtime <psionfile>" << endl;
110 cout << " test <psionfile>" << endl;
111 cout << " gattr <psionfile>" << endl;
112 cout << " sattr [[-|+]rwhsa] <psionfile>" << endl;
113 cout << " devs" << endl;
114 cout << " dir|ls" << endl;
115 cout << " dircnt" << endl;
116 cout << " cd <dir>" << endl;
117 cout << " lcd <dir>" << endl;
118 cout << " !<system command>" << endl;
119 cout << " get <psionfile>" << endl;
120 cout << " put <unixfile>" << endl;
121 cout << " mget <shellpattern>" << endl;
122 cout << " mput <shellpattern>" << endl;
123 cout << " cp <psionfile> <psionfile>" << endl;
124 cout << " del|rm <psionfile>" << endl;
125 cout << " mkdir <psiondir>" << endl;
126 cout << " rmdir <psiondir>" << endl;
127 cout << " volname <drive> <name>" << endl;
128 cout << " prompt" << endl;
129 cout << " hash" << endl;
130 cout << " bye" << endl;
131 cout << endl << _("Known RPC commands:") << endl << endl;
132 cout << " ps" << endl;
133 cout << " kill <pid|'all'>" << endl;
134 cout << " getclip <unixfile>" << endl;
135 cout << " putclip <unixfile>" << endl;
136 cout << " run <psionfile> [args]" << endl;
137 cout << " killsave <unixfile>" << endl;
138 cout << " runrestore <unixfile>" << endl;
139 cout << " machinfo" << endl;
140 cout << " ownerinfo" << endl;
141 cout << " settime" << endl;
142 cout << " setupinfo" << endl;
143}
144
145static char *join_string_vector(vector<char *> argv, const char *sep) {
146 struct string_buffer sb;
147 sb_init(&sb);
148 int argc = argv.size();
149 for (int i = 0; i < argc; i++) {
150 sb_append(&sb, argv[i]);
151 if (i < argc - 1) {
152 sb_append(&sb, sep);
153 }
154 }
155 return sb_dupfree(&sb);
156}
157
158static int checkAbortNoHash(void *, uint32_t) {
159 return continueRunning;
160}
161
162static int checkAbortHash(void *, uint32_t) {
163 if (continueRunning) {
164 printf("#"); fflush(stdout);
165 }
166 return continueRunning;
167}
168
169static void sigint_handler(int) {
170 continueRunning = 0;
171 signal(SIGINT, sigint_handler);
172}
173
174static void sigint_handler2(int) {
175 continueRunning = 0;
176 fclose(stdin);
177 signal(SIGINT, sigint_handler2);
178}
179
180static int stopPrograms(RPCS &rpcs, const char *file) {
182 processList tmp;
183 FILE *fp = fopen(file, "w");
184
185 if (fp == NULL) {
186 cerr << _("Could not open command list file ") << file << endl;
187 return 1;
188 }
189 fputs("#plpftp processlist\n", fp);
190 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
191 cerr << _("Could not get process list, Error: ") << res << endl;
192 return 1;
193 }
194 for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) {
195 fputs(i->getArgs(), fp);
196 fputc('\n', fp);
197 }
198 fclose(fp);
199 time_t tstart = time(nullptr) + 5;
200 while (!tmp.empty()) {
201 for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) {
202 rpcs.stopProgram(i->getProcId());
203 }
204 usleep(100000);
205 if (time(nullptr) > tstart) {
206 cerr << _("Could not stop all processes. Please stop running\n"
207 "programs manually on the Psion, then hit return.") << flush;
208 cin.getline((char *)&tstart, 1);
209 tstart = time(nullptr) + 5;
210 }
211 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
212 cerr << _("Could not get process list, Error: ") << res << endl;
213 return 1;
214 }
215 }
216 return 0;
217}
218
219static char *get_upto(FILE *fp, const char *term, size_t *final_len) {
220 size_t len = 256;
221 int c;
222 char *l = (char *)malloc(len), *s = l;
223
224 assert(l);
225 for (c = getc(fp); c != EOF && strchr(term, c) == NULL; c = getc(fp)) {
226 if (s == l + len) {
227 l = (char *)realloc(l, len * 2);
228 assert(l);
229 len *= 2;
230 }
231 *s++ = c;
232 }
233 if (s == l + len) {
234 l = (char *)realloc(l, len + 1);
235 assert(l);
236 }
237
238 if (final_len)
239 *final_len = s - l;
240
241 *s++ = '\0';
242 l = (char *)realloc(l, s - l);
243 assert(l);
244 return l;
245}
246
247static char *getln(FILE *fp) {
248 return get_upto(fp, "\n", NULL);
249}
250
251static int startPrograms(RPCS & r, RFSV &a, const char *file) {
253 FILE *fp = fopen(file, "r");
254 string cmd;
255
256 if (fp == NULL) {
257 cerr << _("Could not open command list file ") << file << endl;
258 return 1;
259 }
260 cmd = string(getln(fp));
261 if (strcmp(cmd.c_str(), "#plpftp processlist")) {
262 fclose(fp);
263 cerr << _("Error: ") << file <<
264 _(" is not a process list saved with killsave") << endl;
265 return 1;
266 }
267 for (cmd = string(getln(fp)); cmd.length() > 0; cmd = string(getln(fp))) {
268 int firstBlank = cmd.find(' ');
269 string prog = string(cmd, 0, firstBlank);
270 string arg = string(cmd, firstBlank + 1);
271
272 if (!prog.empty()) {
273 // Workaround for broken programs like Backlite. These do not store
274 // the full program path. In that case we try running the arg1 which
275 // results in starting the program via recog. facility.
276 if ((arg.size() > 2) && (arg[1] == ':') && (arg[0] >= 'A') &&
277 (arg[0] <= 'Z'))
278 res = r.execProgram(arg.c_str(), "");
279 else
280 res = r.execProgram(prog.c_str(), arg.c_str());
281 if (res != RFSV::E_PSI_GEN_NONE) {
282 // If we got an error here, that happened probably because
283 // we have no path at all (e.g. Macro5) and the program is
284 // not registered in the Psion's path properly. Now try
285 // the usual \System\Apps<AppName><AppName>.app
286 // on all drives.
287 if (prog.find('\\') == prog.npos) {
288 uint32_t devbits;
289 if ((res = a.devlist(devbits)) == RFSV::E_PSI_GEN_NONE) {
290 int i;
291 for (i = 0; i < 26; i++) {
292 if (devbits & (1 << i)) {
293 string tmp = string();
294 tmp += ('A' + i);
295 tmp += ":\\System\\Apps\\" + prog + "\\" + prog + ".app";
296 res = r.execProgram(tmp.c_str(), "");
297 if (res == RFSV::E_PSI_GEN_NONE)
298 break;
299 }
300 }
301 }
302 }
303 }
304 if (res != RFSV::E_PSI_GEN_NONE) {
305 cerr << _("Could not start ") << cmd << endl;
306 cerr << _("Error: ") << res << endl;
307 }
308 }
309 }
310 return 0;
311}
312
314 if (a.getProtocolVersion() == 3) {
315 cerr << _("Clipboard protocol not supported by Psion Series 3.") << endl;
316 return false;
317 }
318
320 if ((ret = rc.initClipbd()) == RFSV::E_PSI_GEN_NONE) {
321 return true;
322 } else if (ret == RFSV::E_PSI_GEN_NSUP) {
323 cerr << _("Your Psion does not support the clipboard protocol.\n\
324 The reason for that is usually a missing server library.\n\
325 Make sure that C:\\System\\Libs\\clipsvr.rsy exists.\n\
326 This file is part of PsiWin and usually gets copied to\n\
327 your Psion when you enable CopyAnywhere in PsiWin.\n\
328 You can also get it from a PsiWin installation directory\n\
329 and copy it to your Psion manually.") << endl;
330 }
331 return false;
332}
333
334static void psiText2ascii(char *buf, int len) {
335 char *p;
336
337 for (p = buf; len; len--, p++)
338 switch (*p) {
339 case 6:
340 case 7:
341 *p = '\n';
342 break;
343 case 8:
344 *p = '\f';
345 break;
346 case 10:
347 *p = '\t';
348 break;
349 case 11:
350 case 12:
351 *p = '-';
352 break;
353 case 15:
354 case 16:
355 *p = ' ';
356 break;
357 }
358}
359
360static void ascii2PsiText(char *buf, int len) {
361 char *p;
362
363 for (p = buf; len; len--, p++)
364 switch (*p) {
365 case '\0':
366 *p = ' ';
367 case '\n':
368 *p = 6;
369 break;
370 case '\f':
371 *p = 8;
372 break;
373 case '-':
374 *p = 11;
375 break;
376 }
377}
378
379static char *slurp(FILE *fp, size_t *final_len) {
380 return get_upto(fp, "", final_len);
381}
382
383int FTP::putClipText(RFSV &a, rclip & rc, const char *file) {
385 uint32_t fh;
386 uint32_t l;
387 const unsigned char *p;
388 BufferStore b;
389 char *data;
390 FILE *fp;
391
392 if (!checkClipConnection(a, rc)) {
393 return 1;
394 }
395
396 if ((fp = fopen(file, "r")) == NULL) {
397 return 1;
398 }
399
400 size_t len;
401 data = slurp(fp, &len);
402 fclose(fp);
403 ascii2PsiText(data, len);
404
405 res = a.freplacefile(0x200, CLIPFILE, fh);
406 if (res == RFSV::E_PSI_GEN_NONE) {
407 // Base Header
408 b.addDWord(0x10000037); // @00 UID 0
409 b.addDWord(0x1000003b); // @04 UID 1
410 b.addDWord(0); // @08 UID 3
411 b.addDWord(0x4739d53b); // @0c Checksum the above
412
413 // Section Table
414 b.addDWord(0x00000014); // @10 Offset of Section Table
415 b.addByte(2); // @14 Section Table, length in DWords
416 b.addDWord(0x10000033); // @15 Section Type (ASCII)
417 b.addDWord(0x0000001d); // @19 Section Offset
418
419 // Data
420 b.addDWord(strlen(data)); // @1e Section (String) length
421 b.addStringT(data); // @22 Data (Psion Word seems to need a
422 // terminating 0.
423
424 p = (const unsigned char *)b.getString(0);
425 a.fwrite(fh, p, b.getLen(), l);
426 a.fclose(fh);
427 a.fsetattr(CLIPFILE, 0x20, 0x07);
428 }
429
430 return 0;
431}
432
433int FTP::getClipData(RFSV &a, rclip &rc, const char *file) {
435 PlpDirent de;
436 uint32_t fh;
437 string clipText;
438
439 if (!checkClipConnection(a, rc)) {
440 return 1;
441 }
442
443 res = a.fgeteattr(CLIPFILE, de);
444 if (res == RFSV::E_PSI_GEN_NONE) {
445 if (de.getAttr() & RFSV::PSI_A_ARCHIVE) {
446 uint32_t len = de.getSize();
447 char *buf = (char *)malloc(len);
448
449 if (!buf) {
450 cerr << "Out of memory in getClipData" << endl;
451 return 1;
452 }
454 CLIPFILE, fh);
455 if (res == RFSV::E_PSI_GEN_NONE) {
456 uint32_t tmp;
457 res = a.fread(fh, (unsigned char *)buf, len, tmp);
458 a.fclose(fh);
459
460 if ((res == RFSV::E_PSI_GEN_NONE) && (tmp == len)) {
461 char *p = buf;
462 int lcount;
463 uint32_t *ti = (uint32_t*)buf;
464
465 // Check base header
466 if (*ti++ != 0x10000037) {
467 free(buf);
468 return 1;
469 }
470 if (*ti++ != 0x1000003b) {
471 free(buf);
472 return 1;
473 }
474 if (*ti++ != 0) {
475 free(buf);
476 return 1;
477 }
478 if (*ti++ != 0x4739d53b) {
479 free(buf);
480 return 1;
481 }
482
483 // Start of section table
484 p = buf + *ti;
485 // Length of section table (in DWords)
486 lcount = *p++;
487
488 uint32_t *td = (uint32_t*)p;
489 while (lcount > 0) {
490 uint32_t sType = *td++;
491 if (sType == 0x10000033) {
492 // An ASCII section
493 p = buf + *td;
494 len = *((uint32_t*)p);
495 p += 4;
496 psiText2ascii(p, len);
497 clipText += (char *)p;
498 }
499 if (sType == 0x1000003d) {
500 // FIXME: Implement this
501 }
502 td++;
503 lcount -= 2;
504 }
505 }
506
507 }
508 free(buf);
509 }
510 }
511
512 FILE *fp = fopen(file, "w");
513 if (fp == NULL) {
514 return 1;
515 }
516 fwrite(clipText.c_str(), 1, clipText.length(), fp);
517 return 0;
518}
519
523static char *epoc_dir_from(const char *path) {
524
525 // Resolve the path against the current remote working directory (global).
526 char *f1 = pathutils::resolve_epoc_path(path, psionDir);
527
528 // Ensure path ends with a slash.
529 if ((f1[strlen(f1) - 1] != '/') && (f1[strlen(f1) - 1] != '\\')) {
530 char *f2 = xasprintf("%s%s", f1, "\\");
531 free(f1);
532 f1 = f2;
533 }
534
535 return f1;
536}
537
538int FTP::session(RFSV &rfsv, RPCS &rpcs, rclip &clipboard, vector<char *> argv) {
540 bool prompt = true;
541 bool hash = false;
543 bool once = false;
544
545 unsigned argc = argv.size();
546 if (argc > 0) {
547 once = true;
548 }
549
550 {
551 Enum<RPCS::machs> machType;
552 BufferArray b;
553 if ((res = rpcs.getOwnerInfo(b)) == RFSV::E_PSI_GEN_NONE) {
554 rpcs.getMachineType(machType);
555 if (!once) {
556 int speed = rfsv.getSpeed();
557 cout << _("Connected to a ") << machType << _(" at ")
558 << speed << _(" baud, OwnerInfo:") << endl;
559 while (!b.empty()) {
560 cout << " " << b.pop().getString() << endl;
561 }
562 cout << endl;
563 }
564 } else {
565 cerr << _("OwnerInfo returned error ") << res << endl;
566 }
567 }
568
569 if (!strcmp(DDRIVE, "AUTO")) {
570 strcpy(defDrive, "::");
572 } else {
573 strcpy(defDrive, DDRIVE);
574 }
575 free(psionDir);
576 psionDir = xasprintf("%s%s", defDrive, DBASEDIR);
577 comp_a = &rfsv;
578 if (!once) {
579 cout << _("Psion dir is: \"") << psionDir << "\"" << endl;
580 initReadline();
581 }
582 continueRunning = 1;
583 signal(SIGINT, sigint_handler);
584 do {
585 if (!once) {
586 argv = getCommand();
587 argc = argv.size();
588 }
589
590 if (argc == 0) {
591 continue;
592 }
593 if ((!strcmp(argv[0], "help")) || (!strcmp(argv[0], "?"))) {
594 usage();
595 continue;
596 }
597 if (!strcmp(argv[0], "prompt")) {
598 prompt = !prompt;
599 cout << _("Prompting now ") << (prompt? _("on") : _("off")) << endl;
600 continue;
601 }
602 if (!strcmp(argv[0], "hash")) {
603 hash = !hash;
604 cout << _("Hash printing now ") << (hash? _("on") : _("off")) << endl;
605 cab = (hash) ? checkAbortHash : checkAbortNoHash;
606 continue;
607 }
608 if (!strcmp(argv[0], "pwd")) {
609 cout << _("Local dir: \"") << localDir << "\"" << endl;
610 cout << _("Psion dir: \"") << psionDir << "\"" << endl;
611 continue;
612 }
613 if (!strcmp(argv[0], "volname") && (argc == 3) && (strlen(argv[1]) == 1)) {
614 if ((res = rfsv.setVolumeName(toupper(argv[1][0]), argv[2])) != RFSV::E_PSI_GEN_NONE) {
615 cerr << _("Error: ") << res << endl;
616 }
617 continue;
618 }
619 if (!strcmp(argv[0], "ren") && (argc == 3)) {
620 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
621 char *f2 = xasprintf("%s%s", psionDir, argv[2]);
622 if ((res = rfsv.rename(f1, f2)) != RFSV::E_PSI_GEN_NONE) {
623 cerr << _("Error: ") << res << endl;
624 }
625 free(f1);
626 free(f2);
627 continue;
628 }
629 if (!strcmp(argv[0], "cp") && (argc == 3)) {
630 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
631 char *f2 = xasprintf("%s%s", psionDir, argv[2]);
632 if ((res = rfsv.copyOnPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
633 cerr << _("Error: ") << res << endl;
634 }
635 free(f1);
636 free(f2);
637 continue;
638 }
639 if (!strcmp(argv[0], "touch") && (argc == 2)) {
640 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
641 PsiTime pt;
642 if ((res = rfsv.fsetmtime(f1, pt)) != RFSV::E_PSI_GEN_NONE) {
643 cerr << _("Error: ") << res << endl;
644 }
645 free(f1);
646 continue;
647 }
648 if (!strcmp(argv[0], "test") && (argc == 2)) {
649 PlpDirent e;
650 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
651 if ((res = rfsv.fgeteattr(f1, e)) != RFSV::E_PSI_GEN_NONE) {
652 cerr << _("Error: ") << res << endl;
653 } else {
654 cout << e << endl;
655 }
656 free(f1);
657 continue;
658 }
659 if (!strcmp(argv[0], "gattr") && (argc == 2)) {
660 uint32_t attr;
661 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
662 if ((res = rfsv.fgetattr(f1, attr)) != RFSV::E_PSI_GEN_NONE) {
663 cerr << _("Error: ") << res << endl;
664 } else {
665 cout << hex << setw(4) << setfill('0') << attr;
666 cout << " (" << rfsv.attr2String(attr) << ")" << endl;
667 }
668 free(f1);
669 continue;
670 }
671 if (!strcmp(argv[0], "gtime") && (argc == 2)) {
672 PsiTime mtime;
673 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
674 if ((res = rfsv.fgetmtime(f1, mtime)) != RFSV::E_PSI_GEN_NONE) {
675 cerr << _("Error: ") << res << endl;
676 } else {
677 cout << mtime << "(" << hex
678 << setw(8) << setfill('0') << mtime.getPsiTimeHi()
679 << ":"
680 << setw(8) << setfill('0') << mtime.getPsiTimeLo()
681 << ")" << endl;
682 }
683 free(f1);
684 continue;
685 }
686 if (!strcmp(argv[0], "sattr") && (argc == 3)) {
687 long attr[2];
688 int aidx = 0;
689 char *p = argv[1];
690
691 char *f1 = xasprintf("%s%s", psionDir, argv[2]);
692
693 attr[0] = attr[1] = 0;
694 while (*p) {
695 switch (*p) {
696 case '+':
697 aidx = 0;
698 break;
699 case '-':
700 aidx = 1;
701 break;
702 case 'r':
703 attr[aidx] |= RFSV::PSI_A_READ;
704 attr[aidx] &= ~RFSV::PSI_A_READ;
705 break;
706 case 'w':
707 attr[1 - aidx] |= RFSV::PSI_A_RDONLY;
708 attr[aidx] &= ~RFSV::PSI_A_RDONLY;
709 break;
710 case 'h':
711 attr[aidx] |= RFSV::PSI_A_HIDDEN;
712 attr[1 - aidx] &= ~RFSV::PSI_A_HIDDEN;
713 break;
714 case 's':
715 attr[aidx] |= RFSV::PSI_A_SYSTEM;
716 attr[1 - aidx] &= ~RFSV::PSI_A_SYSTEM;
717 break;
718 case 'a':
719 attr[aidx] |= RFSV::PSI_A_ARCHIVE;
720 attr[1 - aidx] &= ~RFSV::PSI_A_ARCHIVE;
721 break;
722 }
723 p++;
724 }
725 if ((res = rfsv.fsetattr(f1, attr[0], attr[1])) != RFSV::E_PSI_GEN_NONE) {
726 cerr << _("Error: ") << res << endl;
727 }
728 free(f1);
729 continue;
730 }
731 if (!strcmp(argv[0], "dircnt")) {
732 uint32_t cnt;
733 if ((res = rfsv.dircount(psionDir, cnt)) != RFSV::E_PSI_GEN_NONE) {
734 cerr << _("Error: ") << res << endl;
735 } else {
736 cout << cnt << _(" Entries") << endl;
737 }
738 continue;
739 }
740 if (!strcmp(argv[0], "devs")) {
741 uint32_t devbits;
742 if ((res = rfsv.devlist(devbits)) == RFSV::E_PSI_GEN_NONE) {
743 cout << _("Drive Type Volname Total Free UniqueID") << endl;
744 for (int i = 0; i < 26; i++) {
745 Drive drive;
746
747 if ((devbits & 1) != 0) {
748 if (rfsv.devinfo(i + 'A', drive) == RFSV::E_PSI_GEN_NONE) {
749 cout << (char) ('A' + i) << " " << hex
750 << setw(4) << setfill('0')
751 << static_cast<uint32_t>(drive.getMediaType()) << " " << setw(12)
752 << setfill(' ') << setiosflags(ios::left)
753 << drive.getName().c_str()
754 << resetiosflags(ios::left) << dec << setw(9)
755 << drive.getSize() << setw(9)
756 << drive.getSpace() << " " << setw(8)
757 << setfill('0') << hex << drive.getUID()
758 << dec << endl;
759 }
760 }
761 devbits >>= 1;
762 }
763 } else {
764 cerr << _("Error: ") << res << endl;
765 }
766 continue;
767 }
768 if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "dir")) {
769 PlpDir files;
770 char *dname = argc > 1 ? epoc_dir_from(argv[1]) : xstrdup(psionDir);
771 if ((res = rfsv.dir(dname, files)) != RFSV::E_PSI_GEN_NONE) {
772 cerr << _("Error: ") << res << endl;
773 } else {
774 while (!files.empty()) {
775 cout << files[0] << endl;
776 files.pop_front();
777 }
778 }
779 free(dname);
780 continue;
781 }
782 if (!strcmp(argv[0], "lcd")) {
783 if (argc == 1) {
784 resetUnixWd();
785 } else {
786 if (chdir(argv[1]) == 0) {
787 resetUnixWd();
788 } else {
789 cerr << _("No such directory") << endl
790 << _("Keeping original directory \"") << localDir << "\"" << endl;
791 }
792 }
793 continue;
794 }
795 if (!strcmp(argv[0], "cd")) {
796 if (argc == 1) {
797 free(psionDir);
798 psionDir = xasprintf("%s%s", defDrive, DBASEDIR);
799 } else {
800 char *newDir = epoc_dir_from(argv[1]);
801 uint32_t tmp;
802 if ((res = rfsv.dircount(newDir, tmp)) != RFSV::E_PSI_GEN_NONE) {
803 cerr << _("Error: ") << res << endl;
804 cerr << _("Keeping original directory \"") << psionDir << "\"" << endl;
805 free(newDir);
806 } else {
807 free(psionDir);
808 psionDir = newDir;
809 }
810 }
811 continue;
812 }
813 if ((!strcmp(argv[0], "get")) && (argc > 1)) {
814 struct timeval stime;
815 struct timeval etime;
816 struct stat stbuf;
817
818 char *f1 = pathutils::resolve_epoc_path(argv[1], psionDir);
819 string basename = pathutils::epoc_basename(string(f1));
820 char *f2 = xasprintf("%s%s%s", localDir, "/", argc == 2 ? basename.c_str() : argv[2]);
821
822 gettimeofday(&stime, nullptr);
823 if ((res = rfsv.copyFromPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
824 if (hash) {
825 cout << endl;
826 }
827 continueRunning = 1;
828 cerr << _("Error: ") << res << endl;
829 } else {
830 if (hash)
831 cout << endl;
832 gettimeofday(&etime, nullptr);
833 long dsec = etime.tv_sec - stime.tv_sec;
834 long dhse = (etime.tv_usec / 10000) -
835 (stime.tv_usec /10000);
836 if (dhse < 0) {
837 dsec--;
838 dhse = 100 + dhse;
839 }
840 float dt = dhse;
841 dt /= 100.0;
842 dt += dsec;
843 stat(f2, &stbuf);
844 float cps = (float)(stbuf.st_size) / dt;
845 cout << _("Transfer complete, (") << dec << stbuf.st_size
846 << _(" bytes in ") << dsec << "."
847 << dhse << _(" secs = ") << cps << " cps)\n";
848 }
849 free(f1);
850 free(f2);
851 continue;
852 } else if ((!strcmp(argv[0], "mget")) && (argc == 2)) {
853 char *pattern = argv[1];
854 PlpDir files;
855 if ((res = rfsv.dir(psionDir, files)) != RFSV::E_PSI_GEN_NONE) {
856 cerr << _("Error: ") << res << endl;
857 continue;
858 }
859 for (size_t i = 0; i < files.size(); i++) {
860 PlpDirent e = files[i];
861 long attr = e.getAttr();
862
863 if (attr & (RFSV::PSI_A_DIR | RFSV::PSI_A_VOLUME)) {
864 continue;
865 }
866 if (fnmatch(pattern, e.getName(), FNM_NOESCAPE) == FNM_NOMATCH) {
867 continue;
868 }
869 cout << _("Get \"") << e.getName() << "\" (y,n): ";
870 bool yes = false;
871 if (prompt) {
872 cout.flush();
873 yes = yesno();
874 } else {
875 yes = true;
876 cout << "y ";
877 cout.flush();
878 }
879 if (yes) {
880 char *f1 = xasprintf("%s%s", psionDir, e.getName());
881 char *f2 = xasprintf("%s%s%s", localDir, "/", e.getName());
882 if ((res = rfsv.copyFromPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
883 if (hash) {
884 cout << endl;
885 }
886 continueRunning = 1;
887 cerr << _("Error: ") << res << endl;
888 break;
889 } else {
890 if (hash) {
891 cout << endl;
892 }
893 cout << _("Transfer complete\n");
894 }
895 free(f1);
896 free(f2);
897 }
898 }
899 continue;
900 }
901 if (!strcmp(argv[0], "put") && (argc >= 2)) {
902 struct timeval stime;
903 struct timeval etime;
904 struct stat stbuf;
905
906 char *f1 = xasprintf("%s%s%s", localDir, "/", argv[1]);
907 char *f2 = xasprintf("%s%s", psionDir, argc == 2 ? argv[1] : argv[2]);
908 gettimeofday(&stime, nullptr);
909 if ((res = rfsv.copyToPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
910 if (hash) {
911 cout << endl;
912 }
913 continueRunning = 1;
914 cerr << _("Error: ") << res << endl;
915 } else {
916 if (hash)
917 cout << endl;
918 gettimeofday(&etime, nullptr);
919 long dsec = etime.tv_sec - stime.tv_sec;
920 long dhse = (etime.tv_usec / 10000) -
921 (stime.tv_usec /10000);
922 if (dhse < 0) {
923 dsec--;
924 dhse = 100 + dhse;
925 }
926 float dt = dhse;
927 dt /= 100.0;
928 dt += dsec;
929 stat(f1, &stbuf);
930 float cps = (float)(stbuf.st_size) / dt;
931 cout << _("Transfer complete, (") << dec << stbuf.st_size
932 << _(" bytes in ") << dsec << "."
933 << dhse << _(" secs = ") << cps << " cps)\n";
934 }
935 free(f1);
936 free(f2);
937 continue;
938 }
939 if ((!strcmp(argv[0], "mput")) && (argc == 2)) {
940 char *pattern = argv[1];
941 DIR *d = opendir(localDir);
942 if (d) {
943 struct dirent *de;
944 do {
945 de = readdir(d);
946 if (de) {
947 struct stat st;
948
949 if (fnmatch(pattern, de->d_name, FNM_NOESCAPE) == FNM_NOMATCH) {
950 continue;
951 }
952 char *f1 = xasprintf("%s%s%s", localDir, "/", de->d_name);
953 if (stat(f1, &st) == 0 && S_ISREG(st.st_mode)) {
954 cout << _("Put \"") << de->d_name << "\" y,n: ";
955 bool yes = false;
956 if (prompt) {
957 cout.flush();
958 yes = yesno();
959 } else {
960 cout << "y ";
961 cout.flush();
962 }
963 if (yes) {
964 char *f2 = xasprintf("%s%s", psionDir, de->d_name);
965 if ((res = rfsv.copyToPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
966 if (hash) {
967 cout << endl;
968 }
969 continueRunning = 1;
970 cerr << _("Error: ") << res << endl;
971 free(f1);
972 free(f2);
973 break;
974 } else {
975 if (hash) {
976 cout << endl;
977 }
978 free(f2);
979 cout << _("Transfer complete\n");
980 }
981 }
982 }
983 free(f1);
984 }
985 } while (de);
986 closedir(d);
987 } else {
988 cerr << _("Error in directory name \"") << localDir << "\"\n";
989 }
990 continue;
991 }
992 if ((!strcmp(argv[0], "del") ||
993 !strcmp(argv[0], "rm")) && (argc == 2)) {
994 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
995 if ((res = rfsv.remove(f1)) != RFSV::E_PSI_GEN_NONE) {
996 cerr << _("Error: ") << res << endl;
997 }
998 free(f1);
999 continue;
1000 }
1001 if (!strcmp(argv[0], "mkdir") && (argc == 2)) {
1002 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
1003 if ((res = rfsv.mkdir(f1)) != RFSV::E_PSI_GEN_NONE) {
1004 cerr << _("Error: ") << res << endl;
1005 }
1006 free(f1);
1007 continue;
1008 }
1009 if (!strcmp(argv[0], "rmdir") && (argc == 2)) {
1010 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
1011 if ((res = rfsv.rmdir(f1)) != RFSV::E_PSI_GEN_NONE) {
1012 cerr << _("Error: ") << res << endl;
1013 }
1014 free(f1);
1015 continue;
1016 }
1017 if (argv[0][0] == '!') {
1018 char *cmd = join_string_vector(argv, " ");
1019 if (strlen(cmd)) {
1020 ignore_value(system(cmd));
1021 } else {
1022 const char *sh;
1023 cout << _("Starting subshell ...\n");
1024 sh = getenv("SHELL");
1025 if (!sh) {
1026 sh = "/bin/sh";
1027 }
1028 ignore_value(system(sh));
1029 }
1030 free(cmd);
1031 continue;
1032 }
1033 // RPCS commands
1034 if (!strcmp(argv[0], "settime")) {
1035 if ((res = rpcs.setTime(time(NULL))) != RFSV::E_PSI_GEN_NONE) {
1036 cerr << _("Error: ") << res << endl;
1037 }
1038 continue;
1039 }
1040 if (!strcmp(argv[0], "setupinfo")) {
1041 Enum<RFSV::errs> res;
1042 BufferStore db;
1043
1044 if ((res = rpcs.configRead(0, db)) != RFSV::E_PSI_GEN_NONE) {
1045 cerr << _("Error: ") << res << endl;
1046 continue;
1047 }
1048 if (db.getLen() < 1152) {
1049 cerr << _("Unknown setup info received") << endl;
1050 continue;
1051 }
1052 cout << _("Setup information:") << endl;
1053 cout << _(" Screen contrast: ") << dec
1054 << db.getDWord(0x4c) + 1 << endl;
1055 cout << _(" Keyboard click: ")
1056 << (db.getDWord(0x200) ?
1057 (db.getDWord(0x204) ? _("high") : _("low")) : _("off")) << endl;
1058 cout << _(" Screen click: ")
1059 << (db.getDWord(0x20c) ?
1060 (db.getDWord(0x210) ? _("high") : _("low")) : _("off")) << endl;
1061 cout << _(" Error sound: ")
1062 << (db.getDWord(0x214) ?
1063 (db.getDWord(0x218) ? _("high") : _("low")) : _("off")) << endl;
1064 cout << _(" Auto-switch off: ");
1065 switch (db.getDWord(0x228)) {
1066 case 0:
1067 cout << _("never");
1068 break;
1069 case 1:
1070 cout << _("if running on battery power");
1071 break;
1072 case 2:
1073 cout << _("always");
1074 break;
1075 }
1076 cout << endl;
1077 if (db.getDWord(0x228) != 0)
1078 cout << _(" Switch off after: ") << dec
1079 << db.getDWord(0x22c) << _(" seconds") << endl;
1080 cout << _(" Backlight off after: ") << dec
1081 << db.getDWord(0x234) << _(" seconds") << endl;
1082 cout << _(" Switch on when tapping on screen: ")
1083 << (db.getDWord(0x238) ? _("yes") : _("no")) << endl;
1084 cout << _(" Switch on when opening: ")
1085 << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl;
1086 cout << _(" Switch off when closing: ")
1087 << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl;
1088 cout << _(" Ask for password on startup: ")
1089 << (db.getDWord(0x29c) ? _("yes") : _("no")) << endl;
1090 cout << _(" Show Owner info on startup: ");
1091 switch (db.getByte(0x3b0)) {
1092 case 0x31:
1093 cout << _("never");
1094 break;
1095 case 0x32:
1096 cout << _("once a day");
1097 break;
1098 case 0x33:
1099 cout << _("always");
1100 break;
1101 }
1102 cout << endl;
1103 continue;
1104 }
1105 if (!strcmp(argv[0], "run") && (argc >= 2)) {
1106 vector<char *> args = {argv.begin() + 1, argv.end()};
1107 char *arg = join_string_vector(args, " ");
1108 char *cmd;
1109 if (!strchr(argv[1], ':')) {
1110 cmd = xasprintf("%s%s", psionDir, argv[1]);
1111 } else {
1112 cmd = xstrdup(argv[1]);
1113 }
1114 rpcs.execProgram(cmd, arg);
1115 free(arg);
1116 free(cmd);
1117 continue;
1118 }
1119 if (!strcmp(argv[0], "ownerinfo")) {
1120 BufferArray b;
1121 if ((res = rpcs.getOwnerInfo(b)) != RFSV::E_PSI_GEN_NONE) {
1122 cerr << _("Error: ") << res << endl;
1123 continue;
1124 }
1125 while (!b.empty()) {
1126 cout << " " << b.pop().getString() << endl;
1127 }
1128 continue;
1129 }
1130 if (!strcmp(argv[0], "machinfo")) {
1132 if ((res = rpcs.getMachineInfo(mi)) != RFSV::E_PSI_GEN_NONE) {
1133 cerr << _("Error: ") << res << endl;
1134 continue;
1135 }
1136
1137 cout << _("General:") << endl;
1138 cout << _(" Machine Type: ") << mi.machineType << endl;
1139 cout << _(" Machine Name: ") << mi.machineName << endl;
1140 cout << _(" Machine UID: ") << hex << mi.machineUID << dec << endl;
1141 cout << _(" UI Language: ") << mi.uiLanguage << endl;
1142 cout << _("ROM:") << endl;
1143 cout << _(" Version: ") << mi.romMajor << "." << setw(2) << setfill('0') <<
1144 mi.romMinor << "(" << mi.romBuild << ")" << endl;
1145 cout << _(" Size: ") << mi.romSize / 1024 << "k" << endl;
1146 cout << _(" Programmable: ") <<
1147 (mi.romProgrammable ? _("yes") : _("no")) << endl;
1148 cout << _("RAM:") << endl;
1149 cout << _(" Size: ") << mi.ramSize / 1024 << "k" << endl;
1150 cout << _(" Free: ") << mi.ramFree / 1024 << "k" << endl;
1151 cout << _(" Free max: ") << mi.ramMaxFree / 1024 << "k" << endl;
1152 cout << _("RAM disk size: ") << mi.ramDiskSize / 1024 << "k" << endl;
1153 cout << _("Registry size: ") << mi.registrySize << endl;
1154 cout << _("Display size: ") << mi.displayWidth << "x" <<
1155 mi.displayHeight << endl;
1156 cout << _("Time:") << endl;
1157 PsiTime pt(&mi.time, &mi.tz);
1158 cout << _(" Current time: ") << pt << endl;
1159 cout << _(" UTC offset: ") << mi.tz.utc_offset << _(" seconds") << endl;
1160 cout << _(" DST: ") <<
1161 (mi.tz.dst_zones & PsiTime::PSI_TZ_HOME ? _("yes") : _("no")) << endl;
1162 cout << _(" Timezone: ") << mi.tz.home_zone << endl;
1163 cout << _(" Country Code: ") << mi.countryCode << endl;
1164 cout << _("Main battery:") << endl;
1166 cout << _(" Changed at: ") << pt << endl;
1167 cout << _(" Used for: ") << mi.mainBatteryUsedTime << endl;
1168 cout << _(" Status: ") << mi.mainBatteryStatus << endl;
1169 cout << _(" Current: ") << mi.mainBatteryCurrent << " mA" << endl;
1170 cout << _(" UsedPower: ") << mi.mainBatteryUsedPower << " mAs" << endl;
1171 cout << _(" Voltage: ") << mi.mainBatteryVoltage << " mV" << endl;
1172 cout << _(" Max. voltage: ") << mi.mainBatteryMaxVoltage << " mV" << endl;
1173 cout << _("Backup battery:") << endl;
1174 cout << _(" Status: ") << mi.backupBatteryStatus << endl;
1175 cout << _(" Voltage: ") << mi.backupBatteryVoltage << " mV" << endl;
1176 cout << _(" Max. voltage: ") << mi.backupBatteryMaxVoltage << " mV" << endl;
1177 cout << _("External power:") << endl;
1178 cout << _(" Supplied: ")
1179 << (mi.externalPower ? _("yes") : _("no")) << endl;
1180 cout << _(" Used for: ") << mi.externalPowerUsedTime << endl;
1181 continue;
1182 }
1183 if (!strcmp(argv[0], "runrestore") && (argc == 2)) {
1184 startPrograms(rpcs, rfsv, argv[1]);
1185 continue;
1186 }
1187 if (!strcmp(argv[0], "killsave") && (argc == 2)) {
1188 stopPrograms(rpcs, argv[1]);
1189 continue;
1190 }
1191 if (!strcmp(argv[0], "putclip") && (argc == 2)) {
1192 if (putClipText(rfsv, clipboard, argv[1])) {
1193 cerr << _("Error setting clipboard") << endl;
1194 }
1195 continue;
1196 }
1197 if (!strcmp(argv[0], "getclip") && (argc == 2)) {
1198 if (getClipData(rfsv, clipboard, argv[1])) {
1199 cerr << _("Error getting clipboard") << endl;
1200 }
1201 continue;
1202 }
1203 if (!strcmp(argv[0], "kill") && (argc >= 2)) {
1204 processList tmp;
1205 bool anykilled = false;
1206 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
1207 cerr << _("Error: ") << res << endl;
1208 } else {
1209 for (unsigned int i = 1; i < argc; i++) {
1210 int kpid;
1211 if (!strcmp(argv[i], "all")) {
1212 kpid = -1;
1213 } else {
1214 sscanf(argv[i], "%d", &kpid);
1215 }
1216 processList::iterator j;
1217 for (j = tmp.begin(); j != tmp.end(); j++) {
1218 if (kpid == -1 || kpid == j->getPID()) {
1219 rpcs.stopProgram(j->getProcId());
1220 anykilled = true;
1221 }
1222 }
1223 if (kpid == -1) {
1224 break;
1225 }
1226 }
1227 if (!anykilled) {
1228 cerr << _("no such process") << endl;
1229 }
1230 }
1231 continue;
1232 }
1233 if (!strcmp(argv[0], "ps")) {
1234 processList tmp;
1235 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
1236 cerr << _("Error: ") << res << endl;
1237 } else {
1238 cout << "PID CMD ARGS" << endl;
1239 for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) {
1240 cout << *i << endl;
1241 }
1242 }
1243 continue;
1244 }
1245 if (strcmp(argv[0], "bye") == 0 || strcmp(argv[0], "quit") == 0) {
1246 continueRunning = 0;
1247 } else {
1248 cerr << _("syntax error. Try \"help\"") << endl;
1249 }
1250 } while (!once && continueRunning);
1251 return rfsv.getStatus();
1252}
1253
1254#define MATCHFUNCTION rl_completion_matches
1255
1256static const char *all_commands[] = {
1257 "pwd", "ren", "touch", "gtime", "test", "gattr", "sattr", "devs",
1258 "dir", "ls", "dircnt", "cd", "lcd", "get", "put", "mget", "mput",
1259 "del", "rm", "mkdir", "rmdir", "prompt", "bye", "cp", "volname",
1260 "ps", "kill", "killsave", "runrestore", "run", "machinfo",
1261 "ownerinfo", "help", "settime", "setupinfo", NULL
1262};
1263
1264static const char *localfile_commands[] = {
1265 "lcd ", "put ", "mput ", "killsave ", "runrestore ", NULL
1266};
1267
1268static const char *remote_dir_commands[] = {
1269 "cd ", "rmdir ", NULL
1270};
1271
1273static long maskAttr;
1274static char *cplPath;
1275
1276static char *filename_generator(const char *text, int state) {
1277 static int len;
1278 string tmp;
1279
1280 if (!state) {
1281 Enum<RFSV::errs> res;
1282 len = strlen(text);
1283 tmp = psionDir;
1284 // cplPath will always be non-NULL here, having been initialized in
1285 // do_completion.
1286 tmp += cplPath;
1287 tmp = RFSV::convertSlash(tmp);
1288 if ((res = comp_a->dir(tmp.c_str(), comp_files)) != RFSV::E_PSI_GEN_NONE) {
1289 cerr << _("Error: ") << res << endl;
1290 return NULL;
1291 }
1292 }
1293 while (!comp_files.empty()) {
1294 PlpDirent e = comp_files.front();
1295 long attr = e.getAttr();
1296
1297 comp_files.pop_front();
1298 if ((attr & maskAttr) == 0) {
1299 continue;
1300 }
1301 tmp = cplPath;
1302 tmp += e.getName();
1303 if (!(strncmp(tmp.c_str(), text, len))) {
1304 if (attr & RFSV::PSI_A_DIR) {
1305 rl_completion_append_character = '\0';
1306 tmp += '/';
1307 }
1308 return (strdup(tmp.c_str()));
1309 }
1310 }
1311 return NULL;
1312}
1313
1314static char *command_generator(const char *text, int state) {
1315 static int idx, len;
1316 const char *name;
1317
1318 if (!state) {
1319 idx = 0;
1320 len = strlen(text);
1321 }
1322 while ((name = all_commands[idx])) {
1323 idx++;
1324 if (!strncmp(name, text, len)) {
1325 return (strdup(name));
1326 }
1327 }
1328 return NULL;
1329}
1330
1331extern "C" {
1332
1333static char * null_completion(const char *, int) {
1334 static char null[1] = "";
1335 return null;
1336}
1337
1338static char **do_completion(const char *text, int start, int) {
1339 char **matches = NULL;
1340
1341 rl_completion_entry_function = null_completion;
1342 rl_completion_append_character = ' ';
1343 rl_attempted_completion_over = 1;
1344 if (start == 0) {
1345 matches = MATCHFUNCTION(text, command_generator);
1346 } else {
1347 int idx = 0;
1348 const char *name;
1349 char *p;
1350
1351 rl_filename_quoting_desired = 1;
1352 while ((name = localfile_commands[idx])) {
1353 idx++;
1354 if (!strncmp(name, rl_line_buffer, strlen(name))) {
1355 rl_completion_entry_function = NULL;
1356 return NULL;
1357 }
1358 }
1359 maskAttr = 0xffff;
1360 idx = 0;
1361 free(cplPath);
1362 cplPath = xstrdup(text);
1363 p = strrchr(cplPath, '/');
1364 if (p) {
1365 *(++p) = '\0';
1366 } else {
1367 cplPath[0] = '\0';
1368 }
1369 while ((name = remote_dir_commands[idx])) {
1370 idx++;
1371 if (!strncmp(name, rl_line_buffer, strlen(name))) {
1373 }
1374 }
1375
1376 matches = MATCHFUNCTION(text, filename_generator);
1377 }
1378 return matches;
1379}
1380
1381}
1382
1384 rl_readline_name = "plpftp";
1385 rl_completion_entry_function = null_completion;
1386 rl_attempted_completion_function = do_completion;
1387 rl_basic_word_break_characters = " \t\n\"\\'`@><=;|&{(";
1388 rl_completer_quote_characters = "\"";
1389}
1390
1391vector<char *> FTP::getCommand() {
1392 int ws, quote;
1393 static char *buf;
1394 vector<char *> argv;
1395
1396 // Free existing buffer, and reinitialize it.
1397 free(buf);
1398 buf = NULL;
1399
1400 // Get new command.
1401 signal(SIGINT, sigint_handler2);
1402 buf = readline("> ");
1403 if (buf) {
1404 add_history(buf);
1405
1406 // Parse command into argv.
1407 ws = 1; quote = 0;
1408 for (char *p = buf; *p; p++)
1409 switch (*p) {
1410 case ' ':
1411 case '\t':
1412 if (!quote) {
1413 ws = 1;
1414 *p = 0;
1415 }
1416 break;
1417 case '"':
1418 quote = 1 - quote;
1419 if (!quote) {
1420 *p = 0;
1421 }
1422 break;
1423 default:
1424 if (ws) {
1425 argv.push_back(p);
1426 }
1427 ws = 0;
1428 }
1429 } else {
1430 cout << "bye" << endl;
1431 buf = strdup("bye");
1432 argv.push_back(buf);
1433 }
1434 signal(SIGINT, sigint_handler);
1435
1436 return argv;
1437}
An array of BufferStores.
Definition: bufferarray.h:30
bool empty() const
Checks if this BufferArray is empty.
Definition: bufferarray.h:164
BufferStore pop(void)
Removes the first BufferStore.
Definition: bufferarray.cc:48
A generic container for an array of bytes.
Definition: bufferstore.h:36
const char * getString(long pos=0) const
Retrieves the characters at index pos.
Definition: bufferstore.cc:118
uint32_t getDWord(long pos=0) const
Retrieves the dword at index pos.
Definition: bufferstore.cc:104
void addByte(unsigned char c)
Appends a byte to the content of this instance.
Definition: bufferstore.cc:157
void addStringT(const char *s)
Appends a string to the content of this instance.
Definition: bufferstore.cc:169
void addDWord(long dw)
Appends a dword to the content of this instance.
Definition: bufferstore.cc:197
unsigned long getLen() const
Retrieves the length of a BufferStore.
Definition: bufferstore.cc:92
unsigned char getByte(long pos=0) const
Retrieves the byte at index pos.
Definition: bufferstore.cc:96
A class representing information about a Disk drive on the psion.
Definition: drive.h:51
uint32_t getUID() const
Retrieve the UID of the drive.
Definition: drive.cc:94
MediaType getMediaType() const
Retrieve the media type of the drive.
Definition: drive.cc:82
uint64_t getSpace() const
Retrieve the free capacity on the drive.
Definition: drive.cc:102
uint64_t getSize() const
Retrieve the total capacity of the drive.
Definition: drive.cc:98
std::string getName() const
Retrieve the volume name of the drive.
Definition: drive.cc:106
Wrapper class featuring range-checking and string representation of enumerated values.
Definition: Enum.h:135
std::vector< char * > getCommand()
Definition: ftp.cc:1391
~FTP()
Definition: ftp.cc:100
int session(RFSV &rfsv, RPCS &rpcs, rclip &clipboard, std::vector< char * > argv)
Definition: ftp.cc:538
int putClipText(RFSV &a, rclip &rc, const char *data)
Definition: ftp.cc:383
void initReadline(void)
Definition: ftp.cc:1383
FTP()
Definition: ftp.cc:96
void resetUnixWd()
Definition: ftp.cc:91
bool checkClipConnection(RFSV &a, rclip &rc)
Definition: ftp.cc:313
int getClipData(RFSV &a, rclip &rc, const char *file)
Definition: ftp.cc:433
void usage()
Definition: ftp.cc:104
char * localDir
Definition: ftp.h:54
char defDrive[9]
Definition: ftp.h:53
A class, representing a directory entry of the Psion.
Definition: plpdirent.h:79
uint32_t getAttr() const
Retrieves the file attributes of a directory entry.
Definition: plpdirent.cc:75
const char * getName() const
Retrieve the file name of a directory entry.
Definition: plpdirent.cc:102
uint32_t getSize() const
Retrieves the file size of a directory entry.
Definition: plpdirent.cc:71
Psion time related utility class.
Definition: psitime.h:124
uint32_t getPsiTimeHi(void)
Retrieves the instance's current value in Psion time format, low 32 bits.
Definition: psitime.cc:144
uint32_t getPsiTimeLo(void)
Retrieves the instance's current value in Psion time format, high 32 bits.
Definition: psitime.cc:140
@ PSI_TZ_HOME
Definition: psitime.h:300
void setPsiTime(psi_timeval *_ptv)
Modifies the value of this instance.
Definition: psitime.cc:108
Access remote file services of a Psion.
Definition: rfsv.h:79
@ PSI_O_RDONLY
Definition: rfsv.h:94
virtual Enum< errs > copyOnPsion(const char *const from, const char *const to, void *, cpCallback_t func)=0
Copies a file from the Psion to the Psion.
virtual Enum< errs > fwrite(const uint32_t handle, const unsigned char *const buffer, const uint32_t len, uint32_t &count)=0
Write to a file on the Psion.
virtual Enum< errs > fgetmtime(const char *const name, PsiTime &mtime)=0
Retrieves the modification time of a file on the Psion.
virtual uint32_t opMode(const uint32_t mode)=0
Converts an open-mode (A combination of the PSI_O_ constants.) from generic representation to the mac...
virtual Enum< errs > remove(const char *const name)=0
Removes a file on the Psion.
virtual Enum< errs > fopen(const uint32_t attr, const char *const name, uint32_t &handle)=0
Opens a file.
std::string attr2String(const uint32_t attr)
Converts a file attribute RFSV::file_attribs to human readable format, usable for showing them in dir...
Definition: rfsv.cc:156
virtual Enum< errs > setVolumeName(const char drive, const char *const name)=0
Set the name of a Psion Volume (Drive).
virtual Enum< errs > fread(const uint32_t handle, unsigned char *const buffer, const uint32_t len, uint32_t &count)=0
Reads from a file on the Psion.
virtual Enum< errs > fsetattr(const char *const name, const uint32_t seta, const uint32_t unseta)=0
virtual Enum< errs > copyToPsion(const char *const from, const char *const to, void *, cpCallback_t func)=0
Copies a file from local machine to the Psion.
virtual Enum< errs > fgeteattr(const char *const name, PlpDirent &e)=0
Retrieves attributes, size and modification time of a file on the Psion.
virtual Enum< errs > fsetmtime(const char *const name, const PsiTime mtime)=0
Sets the modification time of a file on the Psion.
virtual Enum< errs > mkdir(const char *const name)=0
Creates a directory on the Psion.
virtual char defaultInternalDriveLetter()=0
virtual Enum< errs > fclose(const uint32_t handle)=0
Close a file on the Psion whih was previously opened/created by using fopen , fcreatefile ,...
Enum< errs > getStatus()
Retrieves the current connection status.
Definition: rfsv.cc:145
virtual Enum< errs > fgetattr(const char *const name, uint32_t &attr)=0
Retrieves attributes of a file on the Psion.
virtual Enum< errs > dir(const char *const name, PlpDir &ret)=0
Reads a directory on the Psion.
virtual Enum< errs > dircount(const char *const name, uint32_t &count)=0
Counts number of entries in a directory.
virtual Enum< errs > freplacefile(const uint32_t attr, const char *const name, uint32_t &handle)=0
Creates an named file, overwriting an existing file.
virtual int getProtocolVersion()=0
Retrieves the protocol version.
virtual Enum< errs > rmdir(const char *const name)=0
Removes a directory on the Psion.
virtual Enum< errs > devlist(uint32_t &devbits)=0
Retrieves available drives on the Psion.
virtual Enum< errs > devinfo(const char drive, Drive &dinfo)=0
Retrieves details about a drive.
static std::string convertSlash(const std::string &name)
Utility method, converts '/' to '\'.
Definition: rfsv.cc:149
virtual Enum< errs > rename(const char *const oldname, const char *const newname)=0
Renames a file on the Psion.
int getSpeed()
Retrieve speed of serial link.
Definition: rfsv.cc:177
@ E_PSI_GEN_NSUP
Definition: rfsv.h:118
@ E_PSI_GEN_NONE
Definition: rfsv.h:114
virtual Enum< errs > copyFromPsion(const char *from, const char *to, void *context, cpCallback_t func)=0
Copies a file from the Psion to the local machine.
@ PSI_A_ARCHIVE
Definition: rfsv.h:203
@ PSI_A_DIR
Definition: rfsv.h:202
@ PSI_A_HIDDEN
Definition: rfsv.h:200
@ PSI_A_READ
Attributes, valid on SIBO only.
Definition: rfsv.h:212
@ PSI_A_VOLUME
Definition: rfsv.h:204
@ PSI_A_RDONLY
Attributes, valid on both EPOC and SIBO.
Definition: rfsv.h:199
@ PSI_A_SYSTEM
Definition: rfsv.h:201
@ PSI_O_SHARE
Definition: rfsv.h:107
Remote procedure call services via PLP.
Definition: rpcs.h:52
virtual Enum< RFSV::errs > getOwnerInfo(BufferArray &owner)=0
Retrieve owner information of the remote machine.
Enum< RFSV::errs > queryPrograms(processList &ret)
Retrieves a list of all running Programs.
Definition: rpcs.cc:245
virtual Enum< RFSV::errs > getMachineInfo(machineInfo &machineInfo)
Retrieve general Information about the connected machine.
Definition: rpcs.h:354
virtual Enum< RFSV::errs > configRead(uint32_t size, BufferStore &data)
Read from Series 5 scratch RAM.
Definition: rpcs.h:413
Enum< RFSV::errs > stopProgram(const char *program)
Requests termination of a program running on the remote machine.
Definition: rpcs.cc:225
Enum< RFSV::errs > getMachineType(Enum< machs > &type)
Retrieves the type of machine on the remote side as defined in machs.
Definition: rpcs.cc:375
Enum< RFSV::errs > execProgram(const char *program, const char *args)
Starts execution of a program on the remote machine.
Definition: rpcs.cc:199
virtual Enum< RFSV::errs > setTime(time_t time)
Definition: rpcs.h:396
Remote ClipBoard services via PLP.
Definition: rclip.h:44
Enum< RFSV::errs > initClipbd()
Send initialization frame.
Definition: rclip.cc:176
static char ** do_completion(const char *text, int start, int)
Definition: ftp.cc:1338
static char * join_string_vector(vector< char * > argv, const char *sep)
Definition: ftp.cc:145
static int checkAbortHash(void *, uint32_t)
Definition: ftp.cc:162
static int checkAbortNoHash(void *, uint32_t)
Definition: ftp.cc:158
static void ascii2PsiText(char *buf, int len)
Definition: ftp.cc:360
static void psiText2ascii(char *buf, int len)
Definition: ftp.cc:334
static long maskAttr
Definition: ftp.cc:1273
static char * psionDir
Definition: ftp.cc:84
static char * null_completion(const char *, int)
Definition: ftp.cc:1333
static char * epoc_dir_from(const char *path)
Compute new directory from path, which may be absolute or relative, and cwd.
Definition: ftp.cc:523
static char * command_generator(const char *text, int state)
Definition: ftp.cc:1314
static const char * remote_dir_commands[]
Definition: ftp.cc:1268
static int startPrograms(RPCS &r, RFSV &a, const char *file)
Definition: ftp.cc:251
#define MATCHFUNCTION
Definition: ftp.cc:1254
static PlpDir comp_files
Definition: ftp.cc:1272
static char * getln(FILE *fp)
Definition: ftp.cc:247
static RFSV * comp_a
Definition: ftp.cc:85
static const char * all_commands[]
Definition: ftp.cc:1256
static const char * localfile_commands[]
Definition: ftp.cc:1264
static void sigint_handler2(int)
Definition: ftp.cc:174
static int continueRunning
Definition: ftp.cc:86
static char * filename_generator(const char *text, int state)
Definition: ftp.cc:1276
static char * slurp(FILE *fp, size_t *final_len)
Definition: ftp.cc:379
static char * get_upto(FILE *fp, const char *term, size_t *final_len)
Definition: ftp.cc:219
char * readline()
#define CLIPFILE
Definition: ftp.cc:88
static char * cplPath
Definition: ftp.cc:1274
static int stopPrograms(RPCS &rpcs, const char *file)
Definition: ftp.cc:180
static void sigint_handler(int)
Definition: ftp.cc:169
char * resolve_epoc_path(const char *path, const char *initialPath)
Returns a new absolute EPOC path, determined by resolving path relative to initialPath.
Definition: pathutils.cc:116
std::string epoc_basename(std::string path)
Returns the last path component of an EPOC path.
Definition: pathutils.cc:83
Definition: doctest.h:522
static RPCS * r
Definition: main.cc:58
static RFSV * a
Definition: main.cc:55
#define _(String)
Definition: plpintl.h:34
int(* cpCallback_t)(void *, uint32_t)
Defines the callback procedure for progress indication of copy operations.
Definition: rfsv.h:45
std::deque< class PlpDirent > PlpDir
Definition: rfsv.h:34
#define PSI_A_HIDDEN
Definition: rfsv_api.h:49
#define PSI_A_SYSTEM
Definition: rfsv_api.h:50
#define PSI_A_ARCHIVE
Definition: rfsv_api.h:52
#define PSI_A_RDONLY
Definition: rfsv_api.h:48
#define PSI_A_READ
Definition: rfsv_api.h:57
std::vector< PsiProcess > processList
Definition: rpcs.h:35
This struct holds the data returned by RPCS::getMachineInfo.
Definition: rpcs.h:121
unsigned long ramSize
Definition: rpcs.h:134
unsigned long mainBatteryUsedPower
Definition: rpcs.h:150
Enum< languages > uiLanguage
Definition: rpcs.h:126
unsigned long displayHeight
Definition: rpcs.h:141
unsigned long mainBatteryVoltage
Definition: rpcs.h:151
psi_timeval externalPowerUsedTime
Definition: rpcs.h:158
bool externalPower
Definition: rpcs.h:159
bool romProgrammable
Definition: rpcs.h:132
Enum< batterystates > backupBatteryStatus
Definition: rpcs.h:154
psi_timeval mainBatteryInsertionTime
Definition: rpcs.h:146
psi_timeval time
Definition: rpcs.h:143
Enum< machs > machineType
Definition: rpcs.h:122
unsigned short romMajor
Definition: rpcs.h:128
unsigned long ramDiskSize
Definition: rpcs.h:137
unsigned long mainBatteryCurrent
Definition: rpcs.h:149
psi_timeval mainBatteryUsedTime
Definition: rpcs.h:148
Enum< batterystates > mainBatteryStatus
Definition: rpcs.h:147
unsigned short romBuild
Definition: rpcs.h:130
unsigned long countryCode
Definition: rpcs.h:125
unsigned short romMinor
Definition: rpcs.h:129
char machineName[17]
Definition: rpcs.h:123
unsigned long ramMaxFree
Definition: rpcs.h:136
unsigned long long machineUID
Definition: rpcs.h:124
unsigned long backupBatteryMaxVoltage
Definition: rpcs.h:156
unsigned long registrySize
Definition: rpcs.h:139
unsigned long displayWidth
Definition: rpcs.h:140
unsigned long ramFree
Definition: rpcs.h:135
unsigned long mainBatteryMaxVoltage
Definition: rpcs.h:152
psi_timezone tz
Definition: rpcs.h:144
unsigned long romSize
Definition: rpcs.h:131
unsigned long backupBatteryVoltage
Definition: rpcs.h:155
unsigned long home_zone
Definition: psitime.h:97
unsigned long dst_zones
Definition: psitime.h:96
signed long utc_offset
Definition: psitime.h:95