ShellCommands.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. MODULE ShellCommands; (* ejz, *)
  2. IMPORT Machine, Streams, Pipes, AosModules := Modules, Files, Dates, Strings, Commands;
  3. CONST
  4. MaxString = 256;
  5. TYPE
  6. String = ARRAY MaxString OF CHAR;
  7. CmdAlias = POINTER TO RECORD
  8. alias, cmd, help: String;
  9. next: CmdAlias
  10. END;
  11. CmdParameters = POINTER TO ARRAY OF CHAR;
  12. AliasList = OBJECT
  13. VAR alias: CmdAlias;
  14. PROCEDURE Alias(alias, cmd, help: ARRAY OF CHAR);
  15. VAR a: CmdAlias;
  16. BEGIN {EXCLUSIVE}
  17. a := SELF.alias;
  18. WHILE (a # NIL) & (a.alias # alias) DO
  19. a := a.next
  20. END;
  21. IF a = NIL THEN
  22. NEW(a); a.next := SELF.alias; SELF.alias := a; COPY(alias, a.alias)
  23. END;
  24. COPY(cmd, a.cmd); COPY(help, a.help)
  25. END Alias;
  26. PROCEDURE Find(alias: ARRAY OF CHAR): CmdAlias;
  27. VAR a: CmdAlias;
  28. BEGIN {EXCLUSIVE}
  29. a := SELF.alias;
  30. WHILE (a # NIL) & (a.alias # alias) DO
  31. a := a.next
  32. END;
  33. RETURN a
  34. END Find;
  35. PROCEDURE List(out: Streams.Writer);
  36. VAR a: CmdAlias;
  37. BEGIN {EXCLUSIVE}
  38. a := alias;
  39. WHILE a # NIL DO
  40. out.String(a.alias); out.Char(09X); out.String(a.cmd); out.Ln();
  41. IF a.help # "" THEN
  42. out.Char(09X); out.String(a.help); out.Ln()
  43. END;
  44. a := a.next
  45. END
  46. END List;
  47. PROCEDURE &Init*;
  48. BEGIN
  49. alias := NIL;
  50. Alias("alias", "ShellCommands.Alias", "alias [ [ alias cmd ] help ]");
  51. Alias("del", "ShellCommands.Delete", "del { file }");
  52. Alias("dir", "ShellCommands.Directory", "dir [ pattern ]");
  53. Alias("echo", "ShellCommands.Echo", "echo { par }");
  54. Alias("exit", "ShellCommands.Exit", "exit");
  55. Alias("free", "ShellCommands.Free", "free mod");
  56. Alias("help", "ShellCommands.Help", "help [ alias ]");
  57. Alias("mods", "ShellCommands.Modules", "mods");
  58. Alias("start", "ShellCommands.Start", "start cmd { pars }");
  59. Alias("ver", "ShellCommands.Version", "ver")
  60. END Init;
  61. END AliasList;
  62. Context* = OBJECT (Commands.Context)
  63. VAR
  64. alias: AliasList;
  65. C: ANY;
  66. PROCEDURE &New*(C: ANY; in: Streams.Reader; out, err: Streams.Writer);
  67. BEGIN
  68. Init(in, NIL, out, err, NIL);
  69. SELF.C := C; alias := NIL
  70. END New;
  71. END Context;
  72. Command = OBJECT
  73. VAR
  74. ctx: Context;
  75. cmd: String;
  76. next: Command;
  77. PROCEDURE SetContext(C: ANY; in: Streams.Reader; out, err: Streams.Writer);
  78. VAR a: AliasList;
  79. BEGIN
  80. IF (C # ctx.C) OR (in # ctx.in) OR (out # ctx.out) OR (err # ctx.error) THEN
  81. a := ctx.alias;
  82. NEW(ctx, C, in, out, err);
  83. ctx.alias := a
  84. END
  85. END SetContext;
  86. PROCEDURE &Init*(ctx: Context);
  87. BEGIN
  88. ASSERT(ctx # NIL);
  89. SELF.ctx := ctx; cmd := ""; next := NIL
  90. END Init;
  91. END Command;
  92. PROCEDURE GetPar(par: ANY; VAR p: CmdParameters; VAR w: Streams.Writer);
  93. VAR arg : Streams.StringReader; len : LONGINT;
  94. BEGIN
  95. p := NIL; w := NIL;
  96. IF (par # NIL) & (par IS Commands.Context) & (par(Commands.Context).arg IS Streams.StringReader) THEN
  97. arg := par(Commands.Context).arg (Streams.StringReader);
  98. len := arg.Available();
  99. IF (len > 0) THEN
  100. NEW(p, len);
  101. arg.Bytes(p^, 0, len, len);
  102. w := par(Commands.Context).out;
  103. END;
  104. END
  105. END GetPar;
  106. PROCEDURE GetAliasList(p: Commands.Context): AliasList;
  107. BEGIN
  108. IF (p # NIL) & (p IS Context) THEN
  109. RETURN p(Context).alias
  110. END;
  111. RETURN NIL
  112. END GetAliasList;
  113. PROCEDURE Close(p: Commands.Context);
  114. VAR ctx: Context;
  115. BEGIN
  116. IF (p # NIL) & (p IS Context) THEN
  117. ctx := p(Context);
  118. IF ctx.C IS Pipes.Pipe THEN
  119. ctx.out.Update(); ctx.C(Pipes.Pipe).Close()
  120. END
  121. END
  122. END Close;
  123. PROCEDURE Alias*(context : Commands.Context);
  124. VAR al: AliasList; alias, cmd, help: String;
  125. BEGIN
  126. al := GetAliasList(context);
  127. IF context.arg.GetString(alias) & context.arg.GetString(cmd) THEN
  128. context.out.String(alias); context.out.Char(09X); context.out.String(cmd); context.out.Ln;
  129. IF context.arg.GetString(help) THEN
  130. context.out.Char(09X); context.out.String(help); context.out.Ln()
  131. ELSE help := "";
  132. END;
  133. al.Alias(alias, cmd, help);
  134. ELSE
  135. al.List(context.out)
  136. END;
  137. Close(context);
  138. END Alias;
  139. PROCEDURE Delete*(context : Commands.Context);
  140. VAR filename: Files.FileName; res: WORD;
  141. BEGIN
  142. WHILE context.arg.GetString(filename) DO
  143. context.out.String(filename); context.out.Char(09X);
  144. Files.Delete(filename, res);
  145. IF res = Files.Ok THEN
  146. context.out.String("done");
  147. ELSE
  148. context.out.String("error: "); context.out.Int(res, 0)
  149. END;
  150. context.out.Ln;
  151. END;
  152. Close(context);
  153. END Delete;
  154. PROCEDURE Directory*(context : Commands.Context);
  155. VAR
  156. enum: Files.Enumerator;
  157. name: Files.FileName; flags: SET; time, date, size: LONGINT; tdrec: Dates.DateTime; str: ARRAY 32 OF CHAR;
  158. BEGIN
  159. IF ~context.arg.GetString(str) THEN str := "*"; END;
  160. NEW(enum);
  161. enum.Open(str, {Files.EnumSize, Files.EnumTime});
  162. WHILE enum.GetEntry(name, flags, time, date, size) DO
  163. context.out.String(name); context.out.Char(09X); context.out.Int(size, 0); context.out.Char(09X);
  164. tdrec := Dates.OberonToDateTime(date, time);
  165. Strings.TimeToStr(tdrec, str);
  166. context.out.String(str); context.out.Ln()
  167. END;
  168. Close(context);
  169. END Directory;
  170. PROCEDURE Echo*(context : Commands.Context);
  171. VAR in : Streams.Reader;
  172. BEGIN
  173. IF (context.arg.Available() > 0) THEN
  174. in := context.arg;
  175. ELSE
  176. in := context.in;
  177. END;
  178. Streams.Copy (in, context.out);
  179. Close(context);
  180. END Echo;
  181. PROCEDURE Exit*(context : Commands.Context);
  182. VAR ctx: Context;
  183. BEGIN
  184. IF (context # NIL) & (context IS Context) THEN
  185. context.out.Ln(); context.out.String("logout"); context.out.Ln();
  186. ctx := context(Context);
  187. IF ctx.C IS Streams.Connection THEN
  188. ctx.C(Streams.Connection).Close()
  189. END
  190. END;
  191. Close(context);
  192. END Exit;
  193. PROCEDURE Free*(context : Commands.Context);
  194. VAR name: AosModules.Name; msg: String; res: WORD;
  195. BEGIN
  196. IF context.arg.GetString(name) THEN
  197. context.out.String(name); context.out.Char(09X);
  198. AosModules.FreeModule(name, res, msg);
  199. IF res = 0 THEN
  200. context.out.String("done")
  201. ELSE
  202. context.out.Int(res, 0); context.out.String(": "); context.out.String(msg)
  203. END;
  204. context.out.Ln;
  205. END;
  206. Close(context);
  207. END Free;
  208. PROCEDURE Help*(context : Commands.Context);
  209. VAR name: String; al: AliasList; a: CmdAlias;
  210. BEGIN
  211. IF ~context.arg.GetString(name) THEN name := "help" END;
  212. al := GetAliasList(context);
  213. a := al.Find(name);
  214. IF a # NIL THEN
  215. context.out.String(a.help);
  216. ELSE
  217. context.out.String(name); context.out.Char(09X); context.out.String("no such alias");
  218. END;
  219. context.out.Ln;
  220. Close(context);
  221. END Help;
  222. PROCEDURE Modules*(context : Commands.Context);
  223. VAR mod: AosModules.Module;
  224. BEGIN
  225. mod := AosModules.root;
  226. WHILE mod # NIL DO
  227. context.out.String(mod.name); context.out.Char(09X);
  228. context.out.Int(mod.refcnt, 0); context.out.Ln();
  229. mod := mod.next
  230. END;
  231. Close(context);
  232. END Modules;
  233. (*
  234. cmdline = cmdpar { "|" cmdpar } [ ( ">" | ">>" ) file ] .
  235. cmdpar = cmd { par } [ "<" file ] .
  236. *)
  237. PROCEDURE execute(context : Commands.Context; VAR cmdline: ARRAY OF CHAR; flags: SET; VAR res: WORD; VAR msg: ARRAY OF CHAR);
  238. VAR
  239. ctx: Context; R: Streams.StringReader; cmd, prev, cmds: Command; i, j, p, ofs, len: LONGINT; ch, filter: CHAR;
  240. in, pR: Streams.Reader; out, err, pW: Streams.Writer; file: Files.FileName; F, newF: Files.File; fR: Files.Reader;
  241. fW: Files.Writer; pipe: Pipes.Pipe; a: CmdAlias; cmdString : POINTER TO ARRAY OF CHAR;
  242. BEGIN
  243. ctx := context(Context);
  244. len := LEN(cmdline); newF := NIL; cmds := NIL; prev := NIL;
  245. IF ctx.alias = NIL THEN NEW(ctx.alias) END; ofs := 0;
  246. in := ctx.in; out := ctx.out; err := ctx.error; pipe := NIL;
  247. NEW(R, len); R.Set(cmdline); R.SkipWhitespace();
  248. LOOP
  249. NEW(cmd, ctx); R.String(cmd.cmd);
  250. IF prev # NIL THEN
  251. prev.next := cmd;
  252. Streams.OpenReader(pR, pipe.Receive)
  253. ELSE
  254. cmds := cmd; pR := in
  255. END;
  256. prev := cmd;
  257. R.SkipWhitespace(); p := ofs + R.Pos();
  258. IF p < len THEN
  259. i := p; ch := cmdline[i];
  260. WHILE (ch # 0X) & (ch # "|") & (ch # "<") & (ch # ">") DO
  261. INC(i); ch := cmdline[i]
  262. END;
  263. filter := ch
  264. ELSE
  265. i := len; filter := 0X
  266. END;
  267. IF p < i THEN
  268. NEW(cmdString, i-p+2); j := 0;
  269. ch := cmdline[p]; j := 0;
  270. WHILE (ch # 0X) & (p < i) DO
  271. cmdString[j] := ch; INC(j);
  272. INC(p); ch := cmdline[p]
  273. END;
  274. cmdString[j] := " "; INC(j);
  275. cmdString[j] := 0X;
  276. IF (cmd.ctx.arg IS Streams.StringReader) THEN
  277. cmd.ctx.arg(Streams.StringReader).SetRaw(cmdString^, 0, j +1);
  278. END;
  279. ELSE
  280. cmdString := NIL
  281. END;
  282. CASE filter OF
  283. "|": ofs := i+1; R.SetRaw(cmdline, ofs, len-ofs);
  284. NEW(pipe, 1024);
  285. Streams.OpenWriter(pW, pipe.Send);
  286. cmd.SetContext(pipe, pR, pW, err)
  287. |"<": ofs := i+1; R.SetRaw(cmdline, ofs, len-ofs);
  288. R.SkipWhitespace(); R.String(file);
  289. R.SkipWhitespace(); R.Char(ch);
  290. IF ch = "|" THEN
  291. NEW(pipe, 1024);
  292. Streams.OpenWriter(pW, pipe.Send)
  293. ELSE
  294. pipe := NIL
  295. END;
  296. F := Files.Old(file);
  297. IF F # NIL THEN
  298. Files.OpenReader(fR, F, 0);
  299. IF pR # in THEN
  300. res := -1; COPY("invalid command syntax", msg); RETURN
  301. END;
  302. IF pipe # NIL THEN
  303. cmd.SetContext(pipe, fR, pW, err)
  304. ELSE
  305. cmd.SetContext(ctx.C, fR, out, err)
  306. END
  307. ELSE
  308. res := -1; COPY("input file not found", msg); RETURN
  309. END;
  310. IF pipe = NIL THEN
  311. R.SkipWhitespace();
  312. IF R.res # Streams.EOF THEN
  313. res := -1; COPY("invalid command syntax", msg); RETURN
  314. END;
  315. EXIT
  316. END
  317. |">": IF cmdline[i+1] = ">" THEN
  318. ofs := i+2; R.SetRaw(cmdline, ofs, len-ofs);
  319. R.SkipWhitespace(); R.String(file);
  320. F := Files.Old(file);
  321. IF F # NIL THEN
  322. Files.OpenWriter(fW, F, F.Length());
  323. cmd.SetContext(ctx.C, pR, fW, err)
  324. ELSE
  325. res := -1; COPY("ouput file not found", msg); RETURN
  326. END
  327. ELSE
  328. ofs := i+1; R.SetRaw(cmdline, ofs, len-ofs);
  329. R.SkipWhitespace(); R.String(file);
  330. F := Files.New(file);
  331. IF F # NIL THEN
  332. Files.OpenWriter(fW, F, 0);
  333. cmd.SetContext(ctx.C, pR, fW, err);
  334. newF := F
  335. ELSE
  336. res := -1; COPY("ouput file not created", msg); RETURN
  337. END
  338. END;
  339. R.SkipWhitespace();
  340. IF R.res # Streams.EOF THEN
  341. res := -1; COPY("invalid command syntax", msg); RETURN
  342. END;
  343. EXIT
  344. ELSE
  345. cmd.SetContext(ctx.C, pR, out, err); EXIT
  346. END;
  347. R.SkipWhitespace();
  348. IF R.res # Streams.Ok THEN
  349. res := -1; COPY("invalid command syntax", msg); RETURN
  350. END
  351. END;
  352. prev := NIL; cmd := cmds;
  353. WHILE cmd # NIL DO
  354. a := ctx.alias.Find(cmd.cmd);
  355. IF a # NIL THEN COPY(a.cmd, cmd.cmd) END;
  356. IF cmd.next = NIL THEN
  357. Commands.Activate(cmd.cmd, cmd.ctx, flags, res, msg)
  358. ELSE
  359. Commands.Activate(cmd.cmd, cmd.ctx, {}, res, msg)
  360. END;
  361. IF res # 0 THEN RETURN END;
  362. prev := cmd; cmd := cmd.next
  363. END;
  364. IF newF # NIL THEN
  365. prev.ctx.out.Update();
  366. Files.Register(newF)
  367. END
  368. END execute;
  369. PROCEDURE Start*(context : Commands.Context);
  370. VAR msg: String; len: LONGINT; res: WORD; string : POINTER TO ARRAY OF CHAR;
  371. BEGIN
  372. IF (context.arg IS Streams.StringReader) & (context.arg.Available() > 0) THEN
  373. NEW(string, context.arg.Available() +1);
  374. context.arg.Bytes(string^, 0, context.arg.Available(), len);
  375. string[LEN(string)-1] := 0X;
  376. execute(context, string^, {}, res, msg);
  377. IF res # 0 THEN
  378. context.out.Int(res, 0); context.out.String(": "); context.out.String(msg); context.out.Ln()
  379. END
  380. END;
  381. Close(context);
  382. END Start;
  383. PROCEDURE Version*(context : Commands.Context);
  384. BEGIN
  385. context.out.String(Machine.version); context.out.Ln;
  386. Close(context);
  387. END Version;
  388. PROCEDURE Execute*(par: Commands.Context; VAR cmdline: ARRAY OF CHAR; VAR res: WORD; VAR msg: ARRAY OF CHAR);
  389. BEGIN
  390. execute(par, cmdline, {Commands.Wait}, res, msg)
  391. END Execute;
  392. END ShellCommands.