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