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