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