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 rpcs.getMachineType(machType);
553 if (!once) {
554 int speed = rfsv.getSpeed();
555 cout << _("Connected to a ") << machType << _(" at ")
556 << speed << _(" baud.") << endl;
557 cout << endl;
558 }
559 }
560
561 if (!strcmp(DDRIVE, "AUTO")) {
562 strcpy(defDrive, "::");
564 } else {
565 strcpy(defDrive, DDRIVE);
566 }
567 free(psionDir);
568 psionDir = xasprintf("%s%s", defDrive, DBASEDIR);
569 comp_a = &rfsv;
570 if (!once) {
571 cout << _("Psion dir is: \"") << psionDir << "\"" << endl;
572 initReadline();
573 }
574 continueRunning = 1;
575 signal(SIGINT, sigint_handler);
576 do {
577 if (!once) {
578 argv = getCommand();
579 argc = argv.size();
580 }
581
582 if (argc == 0) {
583 continue;
584 }
585 if ((!strcmp(argv[0], "help")) || (!strcmp(argv[0], "?"))) {
586 usage();
587 continue;
588 }
589 if (!strcmp(argv[0], "prompt")) {
590 prompt = !prompt;
591 cout << _("Prompting now ") << (prompt? _("on") : _("off")) << endl;
592 continue;
593 }
594 if (!strcmp(argv[0], "hash")) {
595 hash = !hash;
596 cout << _("Hash printing now ") << (hash? _("on") : _("off")) << endl;
597 cab = (hash) ? checkAbortHash : checkAbortNoHash;
598 continue;
599 }
600 if (!strcmp(argv[0], "pwd")) {
601 cout << _("Local dir: \"") << localDir << "\"" << endl;
602 cout << _("Psion dir: \"") << psionDir << "\"" << endl;
603 continue;
604 }
605 if (!strcmp(argv[0], "volname") && (argc == 3) && (strlen(argv[1]) == 1)) {
606 if ((res = rfsv.setVolumeName(toupper(argv[1][0]), argv[2])) != RFSV::E_PSI_GEN_NONE) {
607 cerr << _("Error: ") << res << endl;
608 }
609 continue;
610 }
611 if (!strcmp(argv[0], "ren") && (argc == 3)) {
612 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
613 char *f2 = xasprintf("%s%s", psionDir, argv[2]);
614 if ((res = rfsv.rename(f1, f2)) != RFSV::E_PSI_GEN_NONE) {
615 cerr << _("Error: ") << res << endl;
616 }
617 free(f1);
618 free(f2);
619 continue;
620 }
621 if (!strcmp(argv[0], "cp") && (argc == 3)) {
622 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
623 char *f2 = xasprintf("%s%s", psionDir, argv[2]);
624 if ((res = rfsv.copyOnPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
625 cerr << _("Error: ") << res << endl;
626 }
627 free(f1);
628 free(f2);
629 continue;
630 }
631 if (!strcmp(argv[0], "touch") && (argc == 2)) {
632 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
633 PsiTime pt;
634 if ((res = rfsv.fsetmtime(f1, pt)) != RFSV::E_PSI_GEN_NONE) {
635 cerr << _("Error: ") << res << endl;
636 }
637 free(f1);
638 continue;
639 }
640 if (!strcmp(argv[0], "test") && (argc == 2)) {
641 PlpDirent e;
642 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
643 if ((res = rfsv.fgeteattr(f1, e)) != RFSV::E_PSI_GEN_NONE) {
644 cerr << _("Error: ") << res << endl;
645 } else {
646 cout << e << endl;
647 }
648 free(f1);
649 continue;
650 }
651 if (!strcmp(argv[0], "gattr") && (argc == 2)) {
652 uint32_t attr;
653 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
654 if ((res = rfsv.fgetattr(f1, attr)) != RFSV::E_PSI_GEN_NONE) {
655 cerr << _("Error: ") << res << endl;
656 } else {
657 cout << hex << setw(4) << setfill('0') << attr;
658 cout << " (" << rfsv.attr2String(attr) << ")" << endl;
659 }
660 free(f1);
661 continue;
662 }
663 if (!strcmp(argv[0], "gtime") && (argc == 2)) {
664 PsiTime mtime;
665 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
666 if ((res = rfsv.fgetmtime(f1, mtime)) != RFSV::E_PSI_GEN_NONE) {
667 cerr << _("Error: ") << res << endl;
668 } else {
669 cout << mtime << "(" << hex
670 << setw(8) << setfill('0') << mtime.getPsiTimeHi()
671 << ":"
672 << setw(8) << setfill('0') << mtime.getPsiTimeLo()
673 << ")" << endl;
674 }
675 free(f1);
676 continue;
677 }
678 if (!strcmp(argv[0], "sattr") && (argc == 3)) {
679 long attr[2];
680 int aidx = 0;
681 char *p = argv[1];
682
683 char *f1 = xasprintf("%s%s", psionDir, argv[2]);
684
685 attr[0] = attr[1] = 0;
686 while (*p) {
687 switch (*p) {
688 case '+':
689 aidx = 0;
690 break;
691 case '-':
692 aidx = 1;
693 break;
694 case 'r':
695 attr[aidx] |= RFSV::PSI_A_READ;
696 attr[aidx] &= ~RFSV::PSI_A_READ;
697 break;
698 case 'w':
699 attr[1 - aidx] |= RFSV::PSI_A_RDONLY;
700 attr[aidx] &= ~RFSV::PSI_A_RDONLY;
701 break;
702 case 'h':
703 attr[aidx] |= RFSV::PSI_A_HIDDEN;
704 attr[1 - aidx] &= ~RFSV::PSI_A_HIDDEN;
705 break;
706 case 's':
707 attr[aidx] |= RFSV::PSI_A_SYSTEM;
708 attr[1 - aidx] &= ~RFSV::PSI_A_SYSTEM;
709 break;
710 case 'a':
711 attr[aidx] |= RFSV::PSI_A_ARCHIVE;
712 attr[1 - aidx] &= ~RFSV::PSI_A_ARCHIVE;
713 break;
714 }
715 p++;
716 }
717 if ((res = rfsv.fsetattr(f1, attr[0], attr[1])) != RFSV::E_PSI_GEN_NONE) {
718 cerr << _("Error: ") << res << endl;
719 }
720 free(f1);
721 continue;
722 }
723 if (!strcmp(argv[0], "dircnt")) {
724 uint32_t cnt;
725 if ((res = rfsv.dircount(psionDir, cnt)) != RFSV::E_PSI_GEN_NONE) {
726 cerr << _("Error: ") << res << endl;
727 } else {
728 cout << cnt << _(" Entries") << endl;
729 }
730 continue;
731 }
732 if (!strcmp(argv[0], "devs")) {
733 uint32_t devbits;
734 if ((res = rfsv.devlist(devbits)) == RFSV::E_PSI_GEN_NONE) {
735 cout << _("Drive Type Volname Total Free UniqueID") << endl;
736 for (int i = 0; i < 26; i++) {
737 Drive drive;
738
739 if ((devbits & 1) != 0) {
740 if (rfsv.devinfo(i + 'A', drive) == RFSV::E_PSI_GEN_NONE) {
741 cout << (char) ('A' + i) << " " << hex
742 << setw(4) << setfill('0')
743 << static_cast<uint32_t>(drive.getMediaType()) << " " << setw(12)
744 << setfill(' ') << setiosflags(ios::left)
745 << drive.getName().c_str()
746 << resetiosflags(ios::left) << dec << setw(9)
747 << drive.getSize() << setw(9)
748 << drive.getSpace() << " " << setw(8)
749 << setfill('0') << hex << drive.getUID()
750 << dec << endl;
751 }
752 }
753 devbits >>= 1;
754 }
755 } else {
756 cerr << _("Error: ") << res << endl;
757 }
758 continue;
759 }
760 if (!strcmp(argv[0], "ls") || !strcmp(argv[0], "dir")) {
761 PlpDir files;
762 char *dname = argc > 1 ? epoc_dir_from(argv[1]) : xstrdup(psionDir);
763 if ((res = rfsv.dir(dname, files)) != RFSV::E_PSI_GEN_NONE) {
764 cerr << _("Error: ") << res << endl;
765 } else {
766 while (!files.empty()) {
767 cout << files[0] << endl;
768 files.pop_front();
769 }
770 }
771 free(dname);
772 continue;
773 }
774 if (!strcmp(argv[0], "lcd")) {
775 if (argc == 1) {
776 resetUnixWd();
777 } else {
778 if (chdir(argv[1]) == 0) {
779 resetUnixWd();
780 } else {
781 cerr << _("No such directory") << endl
782 << _("Keeping original directory \"") << localDir << "\"" << endl;
783 }
784 }
785 continue;
786 }
787 if (!strcmp(argv[0], "cd")) {
788 if (argc == 1) {
789 free(psionDir);
790 psionDir = xasprintf("%s%s", defDrive, DBASEDIR);
791 } else {
792 char *newDir = epoc_dir_from(argv[1]);
793 uint32_t tmp;
794 if ((res = rfsv.dircount(newDir, tmp)) != RFSV::E_PSI_GEN_NONE) {
795 cerr << _("Error: ") << res << endl;
796 cerr << _("Keeping original directory \"") << psionDir << "\"" << endl;
797 free(newDir);
798 } else {
799 free(psionDir);
800 psionDir = newDir;
801 }
802 }
803 continue;
804 }
805 if ((!strcmp(argv[0], "get")) && (argc > 1)) {
806 struct timeval stime;
807 struct timeval etime;
808 struct stat stbuf;
809
810 char *f1 = pathutils::resolve_epoc_path(argv[1], psionDir);
811 string basename = pathutils::epoc_basename(string(f1));
812 char *f2 = xasprintf("%s%s%s", localDir, "/", argc == 2 ? basename.c_str() : argv[2]);
813
814 gettimeofday(&stime, nullptr);
815 if ((res = rfsv.copyFromPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
816 if (hash) {
817 cout << endl;
818 }
819 continueRunning = 1;
820 cerr << _("Error: ") << res << endl;
821 } else {
822 if (hash)
823 cout << endl;
824 gettimeofday(&etime, nullptr);
825 long dsec = etime.tv_sec - stime.tv_sec;
826 long dhse = (etime.tv_usec / 10000) -
827 (stime.tv_usec /10000);
828 if (dhse < 0) {
829 dsec--;
830 dhse = 100 + dhse;
831 }
832 float dt = dhse;
833 dt /= 100.0;
834 dt += dsec;
835 stat(f2, &stbuf);
836 float cps = (float)(stbuf.st_size) / dt;
837 cout << _("Transfer complete, (") << dec << stbuf.st_size
838 << _(" bytes in ") << dsec << "."
839 << dhse << _(" secs = ") << cps << " cps)\n";
840 }
841 free(f1);
842 free(f2);
843 continue;
844 } else if ((!strcmp(argv[0], "mget")) && (argc == 2)) {
845 char *pattern = argv[1];
846 PlpDir files;
847 if ((res = rfsv.dir(psionDir, files)) != RFSV::E_PSI_GEN_NONE) {
848 cerr << _("Error: ") << res << endl;
849 continue;
850 }
851 for (size_t i = 0; i < files.size(); i++) {
852 PlpDirent e = files[i];
853 long attr = e.getAttr();
854
855 if (attr & (RFSV::PSI_A_DIR | RFSV::PSI_A_VOLUME)) {
856 continue;
857 }
858 if (fnmatch(pattern, e.getName(), FNM_NOESCAPE) == FNM_NOMATCH) {
859 continue;
860 }
861 cout << _("Get \"") << e.getName() << "\" (y,n): ";
862 bool yes = false;
863 if (prompt) {
864 cout.flush();
865 yes = yesno();
866 } else {
867 yes = true;
868 cout << "y ";
869 cout.flush();
870 }
871 if (yes) {
872 char *f1 = xasprintf("%s%s", psionDir, e.getName());
873 char *f2 = xasprintf("%s%s%s", localDir, "/", e.getName());
874 if ((res = rfsv.copyFromPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
875 if (hash) {
876 cout << endl;
877 }
878 continueRunning = 1;
879 cerr << _("Error: ") << res << endl;
880 break;
881 } else {
882 if (hash) {
883 cout << endl;
884 }
885 cout << _("Transfer complete\n");
886 }
887 free(f1);
888 free(f2);
889 }
890 }
891 continue;
892 }
893 if (!strcmp(argv[0], "put") && (argc >= 2)) {
894 struct timeval stime;
895 struct timeval etime;
896 struct stat stbuf;
897
898 char *f1 = xasprintf("%s%s%s", localDir, "/", argv[1]);
899 char *f2 = xasprintf("%s%s", psionDir, argc == 2 ? argv[1] : argv[2]);
900 gettimeofday(&stime, nullptr);
901 if ((res = rfsv.copyToPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
902 if (hash) {
903 cout << endl;
904 }
905 continueRunning = 1;
906 cerr << _("Error: ") << res << endl;
907 } else {
908 if (hash)
909 cout << endl;
910 gettimeofday(&etime, nullptr);
911 long dsec = etime.tv_sec - stime.tv_sec;
912 long dhse = (etime.tv_usec / 10000) -
913 (stime.tv_usec /10000);
914 if (dhse < 0) {
915 dsec--;
916 dhse = 100 + dhse;
917 }
918 float dt = dhse;
919 dt /= 100.0;
920 dt += dsec;
921 stat(f1, &stbuf);
922 float cps = (float)(stbuf.st_size) / dt;
923 cout << _("Transfer complete, (") << dec << stbuf.st_size
924 << _(" bytes in ") << dsec << "."
925 << dhse << _(" secs = ") << cps << " cps)\n";
926 }
927 free(f1);
928 free(f2);
929 continue;
930 }
931 if ((!strcmp(argv[0], "mput")) && (argc == 2)) {
932 char *pattern = argv[1];
933 DIR *d = opendir(localDir);
934 if (d) {
935 struct dirent *de;
936 do {
937 de = readdir(d);
938 if (de) {
939 struct stat st;
940
941 if (fnmatch(pattern, de->d_name, FNM_NOESCAPE) == FNM_NOMATCH) {
942 continue;
943 }
944 char *f1 = xasprintf("%s%s%s", localDir, "/", de->d_name);
945 if (stat(f1, &st) == 0 && S_ISREG(st.st_mode)) {
946 cout << _("Put \"") << de->d_name << "\" y,n: ";
947 bool yes = false;
948 if (prompt) {
949 cout.flush();
950 yes = yesno();
951 } else {
952 cout << "y ";
953 cout.flush();
954 }
955 if (yes) {
956 char *f2 = xasprintf("%s%s", psionDir, de->d_name);
957 if ((res = rfsv.copyToPsion(f1, f2, NULL, cab)) != RFSV::E_PSI_GEN_NONE) {
958 if (hash) {
959 cout << endl;
960 }
961 continueRunning = 1;
962 cerr << _("Error: ") << res << endl;
963 free(f1);
964 free(f2);
965 break;
966 } else {
967 if (hash) {
968 cout << endl;
969 }
970 free(f2);
971 cout << _("Transfer complete\n");
972 }
973 }
974 }
975 free(f1);
976 }
977 } while (de);
978 closedir(d);
979 } else {
980 cerr << _("Error in directory name \"") << localDir << "\"\n";
981 }
982 continue;
983 }
984 if ((!strcmp(argv[0], "del") ||
985 !strcmp(argv[0], "rm")) && (argc == 2)) {
986 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
987 if ((res = rfsv.remove(f1)) != RFSV::E_PSI_GEN_NONE) {
988 cerr << _("Error: ") << res << endl;
989 }
990 free(f1);
991 continue;
992 }
993 if (!strcmp(argv[0], "mkdir") && (argc == 2)) {
994 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
995 if ((res = rfsv.mkdir(f1)) != RFSV::E_PSI_GEN_NONE) {
996 cerr << _("Error: ") << res << endl;
997 }
998 free(f1);
999 continue;
1000 }
1001 if (!strcmp(argv[0], "rmdir") && (argc == 2)) {
1002 char *f1 = xasprintf("%s%s", psionDir, argv[1]);
1003 if ((res = rfsv.rmdir(f1)) != RFSV::E_PSI_GEN_NONE) {
1004 cerr << _("Error: ") << res << endl;
1005 }
1006 free(f1);
1007 continue;
1008 }
1009 if (argv[0][0] == '!') {
1010 char *cmd = join_string_vector(argv, " ");
1011 if (strlen(cmd)) {
1012 ignore_value(system(cmd));
1013 } else {
1014 const char *sh;
1015 cout << _("Starting subshell ...\n");
1016 sh = getenv("SHELL");
1017 if (!sh) {
1018 sh = "/bin/sh";
1019 }
1020 ignore_value(system(sh));
1021 }
1022 free(cmd);
1023 continue;
1024 }
1025 // RPCS commands
1026 if (!strcmp(argv[0], "settime")) {
1027 if ((res = rpcs.setTime(time(NULL))) != RFSV::E_PSI_GEN_NONE) {
1028 cerr << _("Error: ") << res << endl;
1029 }
1030 continue;
1031 }
1032 if (!strcmp(argv[0], "setupinfo")) {
1033 Enum<RFSV::errs> res;
1034 BufferStore db;
1035
1036 if ((res = rpcs.configRead(0, db)) != RFSV::E_PSI_GEN_NONE) {
1037 cerr << _("Error: ") << res << endl;
1038 continue;
1039 }
1040 if (db.getLen() < 1152) {
1041 cerr << _("Unknown setup info received") << endl;
1042 continue;
1043 }
1044 cout << _("Setup information:") << endl;
1045 cout << _(" Screen contrast: ") << dec
1046 << db.getDWord(0x4c) + 1 << endl;
1047 cout << _(" Keyboard click: ")
1048 << (db.getDWord(0x200) ?
1049 (db.getDWord(0x204) ? _("high") : _("low")) : _("off")) << endl;
1050 cout << _(" Screen click: ")
1051 << (db.getDWord(0x20c) ?
1052 (db.getDWord(0x210) ? _("high") : _("low")) : _("off")) << endl;
1053 cout << _(" Error sound: ")
1054 << (db.getDWord(0x214) ?
1055 (db.getDWord(0x218) ? _("high") : _("low")) : _("off")) << endl;
1056 cout << _(" Auto-switch off: ");
1057 switch (db.getDWord(0x228)) {
1058 case 0:
1059 cout << _("never");
1060 break;
1061 case 1:
1062 cout << _("if running on battery power");
1063 break;
1064 case 2:
1065 cout << _("always");
1066 break;
1067 }
1068 cout << endl;
1069 if (db.getDWord(0x228) != 0)
1070 cout << _(" Switch off after: ") << dec
1071 << db.getDWord(0x22c) << _(" seconds") << endl;
1072 cout << _(" Backlight off after: ") << dec
1073 << db.getDWord(0x234) << _(" seconds") << endl;
1074 cout << _(" Switch on when tapping on screen: ")
1075 << (db.getDWord(0x238) ? _("yes") : _("no")) << endl;
1076 cout << _(" Switch on when opening: ")
1077 << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl;
1078 cout << _(" Switch off when closing: ")
1079 << (db.getDWord(0x23c) ? _("yes") : _("no")) << endl;
1080 cout << _(" Ask for password on startup: ")
1081 << (db.getDWord(0x29c) ? _("yes") : _("no")) << endl;
1082 cout << _(" Show Owner info on startup: ");
1083 switch (db.getByte(0x3b0)) {
1084 case 0x31:
1085 cout << _("never");
1086 break;
1087 case 0x32:
1088 cout << _("once a day");
1089 break;
1090 case 0x33:
1091 cout << _("always");
1092 break;
1093 }
1094 cout << endl;
1095 continue;
1096 }
1097 if (!strcmp(argv[0], "run") && (argc >= 2)) {
1098 vector<char *> args = {argv.begin() + 1, argv.end()};
1099 char *arg = join_string_vector(args, " ");
1100 char *cmd;
1101 if (!strchr(argv[1], ':')) {
1102 cmd = xasprintf("%s%s", psionDir, argv[1]);
1103 } else {
1104 cmd = xstrdup(argv[1]);
1105 }
1106 rpcs.execProgram(cmd, arg);
1107 free(arg);
1108 free(cmd);
1109 continue;
1110 }
1111 if (!strcmp(argv[0], "ownerinfo")) {
1112 BufferArray b;
1113 if ((res = rpcs.getOwnerInfo(b)) != RFSV::E_PSI_GEN_NONE) {
1114 cerr << _("Error: ") << res << endl;
1115 continue;
1116 }
1117 while (!b.empty()) {
1118 cout << " " << b.pop().getString() << endl;
1119 }
1120 continue;
1121 }
1122 if (!strcmp(argv[0], "machinfo")) {
1124 if ((res = rpcs.getMachineInfo(mi)) != RFSV::E_PSI_GEN_NONE) {
1125 cerr << _("Error: ") << res << endl;
1126 continue;
1127 }
1128
1129 cout << _("General:") << endl;
1130 cout << _(" Machine Type: ") << mi.machineType << endl;
1131 cout << _(" Machine Name: ") << mi.machineName << endl;
1132 cout << _(" Machine UID: ") << hex << mi.machineUID << dec << endl;
1133 cout << _(" UI Language: ") << mi.uiLanguage << endl;
1134 cout << _("ROM:") << endl;
1135 cout << _(" Version: ") << mi.romMajor << "." << setw(2) << setfill('0') <<
1136 mi.romMinor << "(" << mi.romBuild << ")" << endl;
1137 cout << _(" Size: ") << mi.romSize / 1024 << "k" << endl;
1138 cout << _(" Programmable: ") <<
1139 (mi.romProgrammable ? _("yes") : _("no")) << endl;
1140 cout << _("RAM:") << endl;
1141 cout << _(" Size: ") << mi.ramSize / 1024 << "k" << endl;
1142 cout << _(" Free: ") << mi.ramFree / 1024 << "k" << endl;
1143 cout << _(" Free max: ") << mi.ramMaxFree / 1024 << "k" << endl;
1144 cout << _("RAM disk size: ") << mi.ramDiskSize / 1024 << "k" << endl;
1145 cout << _("Registry size: ") << mi.registrySize << endl;
1146 cout << _("Display size: ") << mi.displayWidth << "x" <<
1147 mi.displayHeight << endl;
1148 cout << _("Time:") << endl;
1149 PsiTime pt(&mi.time, &mi.tz);
1150 cout << _(" Current time: ") << pt << endl;
1151 cout << _(" UTC offset: ") << mi.tz.utc_offset << _(" seconds") << endl;
1152 cout << _(" DST: ") <<
1153 (mi.tz.dst_zones & PsiTime::PSI_TZ_HOME ? _("yes") : _("no")) << endl;
1154 cout << _(" Timezone: ") << mi.tz.home_zone << endl;
1155 cout << _(" Country Code: ") << mi.countryCode << endl;
1156 cout << _("Main battery:") << endl;
1158 cout << _(" Changed at: ") << pt << endl;
1159 cout << _(" Used for: ") << mi.mainBatteryUsedTime << endl;
1160 cout << _(" Status: ") << mi.mainBatteryStatus << endl;
1161 cout << _(" Current: ") << mi.mainBatteryCurrent << " mA" << endl;
1162 cout << _(" UsedPower: ") << mi.mainBatteryUsedPower << " mAs" << endl;
1163 cout << _(" Voltage: ") << mi.mainBatteryVoltage << " mV" << endl;
1164 cout << _(" Max. voltage: ") << mi.mainBatteryMaxVoltage << " mV" << endl;
1165 cout << _("Backup battery:") << endl;
1166 cout << _(" Status: ") << mi.backupBatteryStatus << endl;
1167 cout << _(" Voltage: ") << mi.backupBatteryVoltage << " mV" << endl;
1168 cout << _(" Max. voltage: ") << mi.backupBatteryMaxVoltage << " mV" << endl;
1169 cout << _("External power:") << endl;
1170 cout << _(" Supplied: ")
1171 << (mi.externalPower ? _("yes") : _("no")) << endl;
1172 cout << _(" Used for: ") << mi.externalPowerUsedTime << endl;
1173 continue;
1174 }
1175 if (!strcmp(argv[0], "runrestore") && (argc == 2)) {
1176 startPrograms(rpcs, rfsv, argv[1]);
1177 continue;
1178 }
1179 if (!strcmp(argv[0], "killsave") && (argc == 2)) {
1180 stopPrograms(rpcs, argv[1]);
1181 continue;
1182 }
1183 if (!strcmp(argv[0], "putclip") && (argc == 2)) {
1184 if (putClipText(rfsv, clipboard, argv[1])) {
1185 cerr << _("Error setting clipboard") << endl;
1186 }
1187 continue;
1188 }
1189 if (!strcmp(argv[0], "getclip") && (argc == 2)) {
1190 if (getClipData(rfsv, clipboard, argv[1])) {
1191 cerr << _("Error getting clipboard") << endl;
1192 }
1193 continue;
1194 }
1195 if (!strcmp(argv[0], "kill") && (argc >= 2)) {
1196 processList tmp;
1197 bool anykilled = false;
1198 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
1199 cerr << _("Error: ") << res << endl;
1200 } else {
1201 for (unsigned int i = 1; i < argc; i++) {
1202 int kpid;
1203 if (!strcmp(argv[i], "all")) {
1204 kpid = -1;
1205 } else {
1206 sscanf(argv[i], "%d", &kpid);
1207 }
1208 processList::iterator j;
1209 for (j = tmp.begin(); j != tmp.end(); j++) {
1210 if (kpid == -1 || kpid == j->getPID()) {
1211 rpcs.stopProgram(j->getProcId());
1212 anykilled = true;
1213 }
1214 }
1215 if (kpid == -1) {
1216 break;
1217 }
1218 }
1219 if (!anykilled) {
1220 cerr << _("no such process") << endl;
1221 }
1222 }
1223 continue;
1224 }
1225 if (!strcmp(argv[0], "ps")) {
1226 processList tmp;
1227 if ((res = rpcs.queryPrograms(tmp)) != RFSV::E_PSI_GEN_NONE) {
1228 cerr << _("Error: ") << res << endl;
1229 } else {
1230 cout << "PID CMD ARGS" << endl;
1231 for (processList::iterator i = tmp.begin(); i != tmp.end(); i++) {
1232 cout << *i << endl;
1233 }
1234 }
1235 continue;
1236 }
1237 if (strcmp(argv[0], "bye") == 0 || strcmp(argv[0], "quit") == 0) {
1238 continueRunning = 0;
1239 } else {
1240 cerr << _("syntax error. Try \"help\"") << endl;
1241 }
1242 } while (!once && continueRunning);
1243 return rfsv.getStatus();
1244}
1245
1246#define MATCHFUNCTION rl_completion_matches
1247
1248static const char *all_commands[] = {
1249 "pwd", "ren", "touch", "gtime", "test", "gattr", "sattr", "devs",
1250 "dir", "ls", "dircnt", "cd", "lcd", "get", "put", "mget", "mput",
1251 "del", "rm", "mkdir", "rmdir", "prompt", "bye", "cp", "volname",
1252 "ps", "kill", "killsave", "runrestore", "run", "machinfo",
1253 "ownerinfo", "help", "settime", "setupinfo", NULL
1254};
1255
1256static const char *localfile_commands[] = {
1257 "lcd ", "put ", "mput ", "killsave ", "runrestore ", NULL
1258};
1259
1260static const char *remote_dir_commands[] = {
1261 "cd ", "rmdir ", NULL
1262};
1263
1265static long maskAttr;
1266static char *cplPath;
1267
1268static char *filename_generator(const char *text, int state) {
1269 static int len;
1270 string tmp;
1271
1272 if (!state) {
1273 Enum<RFSV::errs> res;
1274 len = strlen(text);
1275 tmp = psionDir;
1276 // cplPath will always be non-NULL here, having been initialized in
1277 // do_completion.
1278 tmp += cplPath;
1279 tmp = RFSV::convertSlash(tmp);
1280 if ((res = comp_a->dir(tmp.c_str(), comp_files)) != RFSV::E_PSI_GEN_NONE) {
1281 cerr << _("Error: ") << res << endl;
1282 return NULL;
1283 }
1284 }
1285 while (!comp_files.empty()) {
1286 PlpDirent e = comp_files.front();
1287 long attr = e.getAttr();
1288
1289 comp_files.pop_front();
1290 if ((attr & maskAttr) == 0) {
1291 continue;
1292 }
1293 tmp = cplPath;
1294 tmp += e.getName();
1295 if (!(strncmp(tmp.c_str(), text, len))) {
1296 if (attr & RFSV::PSI_A_DIR) {
1297 rl_completion_append_character = '\0';
1298 tmp += '/';
1299 }
1300 return (strdup(tmp.c_str()));
1301 }
1302 }
1303 return NULL;
1304}
1305
1306static char *command_generator(const char *text, int state) {
1307 static int idx, len;
1308 const char *name;
1309
1310 if (!state) {
1311 idx = 0;
1312 len = strlen(text);
1313 }
1314 while ((name = all_commands[idx])) {
1315 idx++;
1316 if (!strncmp(name, text, len)) {
1317 return (strdup(name));
1318 }
1319 }
1320 return NULL;
1321}
1322
1323extern "C" {
1324
1325static char * null_completion(const char *, int) {
1326 static char null[1] = "";
1327 return null;
1328}
1329
1330static char **do_completion(const char *text, int start, int) {
1331 char **matches = NULL;
1332
1333 rl_completion_entry_function = null_completion;
1334 rl_completion_append_character = ' ';
1335 rl_attempted_completion_over = 1;
1336 if (start == 0) {
1337 matches = MATCHFUNCTION(text, command_generator);
1338 } else {
1339 int idx = 0;
1340 const char *name;
1341 char *p;
1342
1343 rl_filename_quoting_desired = 1;
1344 while ((name = localfile_commands[idx])) {
1345 idx++;
1346 if (!strncmp(name, rl_line_buffer, strlen(name))) {
1347 rl_completion_entry_function = NULL;
1348 return NULL;
1349 }
1350 }
1351 maskAttr = 0xffff;
1352 idx = 0;
1353 free(cplPath);
1354 cplPath = xstrdup(text);
1355 p = strrchr(cplPath, '/');
1356 if (p) {
1357 *(++p) = '\0';
1358 } else {
1359 cplPath[0] = '\0';
1360 }
1361 while ((name = remote_dir_commands[idx])) {
1362 idx++;
1363 if (!strncmp(name, rl_line_buffer, strlen(name))) {
1365 }
1366 }
1367
1368 matches = MATCHFUNCTION(text, filename_generator);
1369 }
1370 return matches;
1371}
1372
1373}
1374
1376 rl_readline_name = "plpftp";
1377 rl_completion_entry_function = null_completion;
1378 rl_attempted_completion_function = do_completion;
1379 rl_basic_word_break_characters = " \t\n\"\\'`@><=;|&{(";
1380 rl_completer_quote_characters = "\"";
1381}
1382
1383vector<char *> FTP::getCommand() {
1384 int ws, quote;
1385 static char *buf;
1386 vector<char *> argv;
1387
1388 // Free existing buffer, and reinitialize it.
1389 free(buf);
1390 buf = NULL;
1391
1392 // Get new command.
1393 signal(SIGINT, sigint_handler2);
1394 buf = readline("> ");
1395 if (buf) {
1396 add_history(buf);
1397
1398 // Parse command into argv.
1399 ws = 1; quote = 0;
1400 for (char *p = buf; *p; p++)
1401 switch (*p) {
1402 case ' ':
1403 case '\t':
1404 if (!quote) {
1405 ws = 1;
1406 *p = 0;
1407 }
1408 break;
1409 case '"':
1410 quote = 1 - quote;
1411 if (!quote) {
1412 *p = 0;
1413 }
1414 break;
1415 default:
1416 if (ws) {
1417 argv.push_back(p);
1418 }
1419 ws = 0;
1420 }
1421 } else {
1422 cout << "bye" << endl;
1423 buf = strdup("bye");
1424 argv.push_back(buf);
1425 }
1426 signal(SIGINT, sigint_handler);
1427
1428 return argv;
1429}
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:1383
~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:1375
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:1330
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:1265
static char * psionDir
Definition: ftp.cc:84
static char * null_completion(const char *, int)
Definition: ftp.cc:1325
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:1306
static const char * remote_dir_commands[]
Definition: ftp.cc:1260
static int startPrograms(RPCS &r, RFSV &a, const char *file)
Definition: ftp.cc:251
#define MATCHFUNCTION
Definition: ftp.cc:1246
static PlpDir comp_files
Definition: ftp.cc:1264
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:1248
static const char * localfile_commands[]
Definition: ftp.cc:1256
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:1268
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:1266
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