paste.cpp 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /*
  2. * Copyright (c) 2019-2021, Sergey Bugaev <bugaevc@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Format.h>
  7. #include <AK/String.h>
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibGUI/Application.h>
  10. #include <LibGUI/Clipboard.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <sys/wait.h>
  15. #include <unistd.h>
  16. static void spawn_command(const Vector<const char*>& command, const ByteBuffer& data, const char* state)
  17. {
  18. int pipefd[2];
  19. if (pipe(pipefd) < 0) {
  20. perror("pipe");
  21. return;
  22. }
  23. pid_t pid = fork();
  24. if (pid < 0) {
  25. perror("fork");
  26. return;
  27. } else if (pid == 0) {
  28. // We're the child.
  29. dup2(pipefd[0], 0);
  30. close(pipefd[0]);
  31. close(pipefd[1]);
  32. setenv("CLIPBOARD_STATE", state, true);
  33. execvp(command[0], const_cast<char**>(command.data()));
  34. perror("exec");
  35. exit(1);
  36. }
  37. // We're the parent.
  38. close(pipefd[0]);
  39. FILE* f = fdopen(pipefd[1], "w");
  40. fwrite(data.data(), data.size(), 1, f);
  41. if (ferror(f))
  42. warnln("failed to write data to the pipe: {}", strerror(ferror(f)));
  43. fclose(f);
  44. if (wait(nullptr) < 0)
  45. perror("wait");
  46. }
  47. int main(int argc, char* argv[])
  48. {
  49. bool print_type = false;
  50. bool no_newline = false;
  51. bool watch = false;
  52. Vector<const char*> watch_command;
  53. Core::ArgsParser args_parser;
  54. args_parser.set_general_help("Paste from the clipboard to stdout.");
  55. args_parser.add_option(print_type, "Display the copied type", "print-type", 0);
  56. args_parser.add_option(no_newline, "Do not append a newline", "no-newline", 'n');
  57. args_parser.add_option(watch, "Run a command when clipboard data changes", "watch", 'w');
  58. args_parser.add_positional_argument(watch_command, "Command to run in watch mode", "command", Core::ArgsParser::Required::No);
  59. args_parser.parse(argc, argv);
  60. auto app = GUI::Application::construct(argc, argv);
  61. auto& clipboard = GUI::Clipboard::the();
  62. if (watch) {
  63. watch_command.append(nullptr);
  64. clipboard.on_change = [&](const String&) {
  65. // Technically there's a race here...
  66. auto data_and_type = clipboard.fetch_data_and_type();
  67. if (data_and_type.mime_type.is_null()) {
  68. spawn_command(watch_command, {}, "clear");
  69. } else {
  70. spawn_command(watch_command, data_and_type.data, "data");
  71. }
  72. };
  73. // Trigger it the first time immediately.
  74. clipboard.on_change({});
  75. return app->exec();
  76. }
  77. auto data_and_type = clipboard.fetch_data_and_type();
  78. if (data_and_type.mime_type.is_null()) {
  79. warnln("Nothing copied");
  80. return 1;
  81. }
  82. if (!print_type) {
  83. out("{}", StringView(data_and_type.data));
  84. // Append a newline to text contents, unless the caller says otherwise.
  85. if (data_and_type.mime_type.starts_with("text/") && !no_newline)
  86. outln();
  87. } else {
  88. outln("{}", data_and_type.mime_type);
  89. }
  90. return 0;
  91. }