rvi.cc 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. /* rvi - Revision Control System Interface; editor wrapper */
  2. /* v1.0.0 - v1.0.2 by Melekam Mtsegaye <mtsegaye-fm@rucus.ru.ac.za> */
  3. /* v1.1.0 - v1.1.3 by Jeffrey H. Johnson <trnsz@pobox.com> */
  4. #define RVI_VERSION "v1.1.3 (2021-05-03)"
  5. #include <ctype.h>
  6. #include <errno.h>
  7. #include <limits.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <sys/stat.h>
  12. #include <unistd.h>
  13. #ifdef linux
  14. #include <linux/binfmts.h>
  15. #include <linux/limits.h>
  16. #define D_ARG_MAX MAX_ARG_STRLEN
  17. #else /* ifdef linux */
  18. #define D_ARG_MAX sysconf(_SC_ARG_MAX)
  19. #endif /* linux */
  20. #ifndef D_ARG_MAX
  21. #define D_ARG_MAX 255
  22. #endif /* !D_ARG_MAX */
  23. #ifdef linux
  24. #ifndef PAGE_SIZE
  25. #define PAGE_SIZE sysconf(_SC_PAGESIZE)
  26. #endif /* !PAGE_SIZE */
  27. #endif /* linux */
  28. #define RCSDIR "RCS/\0"
  29. #define RCSEXT ",v\0"
  30. #define UNLOCK "/usr/bin/rcs -u -q \"\0"
  31. #define CHECK_OUT "/usr/bin/co -l -q \"\0"
  32. #define CHECK_OUT_R "/usr/bin/co -q \"\0"
  33. #define CHECK_IN "/usr/bin/ci -u -q \"\0"
  34. #define REEDIT 2
  35. #define ABORT 4
  36. #define DG_YES 6
  37. #define DG_NO 8
  38. #define DG_OTHER 0
  39. void handle_error(int);
  40. void do_filecheck(char *);
  41. int do_filechange_check(char *, struct stat *);
  42. char *exec_cmd(char **, int, int flag = 0);
  43. int dialog();
  44. int
  45. main(
  46. int argc,
  47. char **argv
  48. )
  49. {
  50. fprintf(
  51. stdout,
  52. "%s: Revision Control System Interface %s\n",
  53. argv[0],
  54. RVI_VERSION);
  55. if (argc < 2 || argc > 3)
  56. {
  57. fprintf(stdout, "Usage: %s <filename>\n", argv[0]);
  58. exit(1);
  59. }
  60. do_filecheck(argv[1]);
  61. struct stat fileinfo;
  62. stat(argv[1], &fileinfo);
  63. #ifdef DEBUG
  64. fprintf(
  65. stderr,
  66. "UID: %d EUID:%d GID:%d EGID:%d\n",
  67. fileinfo.st_uid,
  68. getenv("UID"),
  69. fileinfo.st_gid,
  70. getenv("GID"));
  71. #endif /* ifdef DEBUG */
  72. char *editor;
  73. editor = getenv("EDITOR");
  74. if (!editor)
  75. {
  76. fprintf(stderr, "Error: EDITOR variable undefined\n");
  77. exit(1);
  78. }
  79. {
  80. char *params[3];
  81. params[0] = CHECK_OUT;
  82. params[1] = argv[1];
  83. params[2] = "\"";
  84. exec_cmd(params, 3);
  85. struct stat tmp;
  86. stat(argv[1], &tmp);
  87. fileinfo.st_mtime = tmp.st_mtime;
  88. }
  89. do
  90. {
  91. char *params[5];
  92. params[0] = editor;
  93. params[1] = " \0";
  94. params[2] = "\"";
  95. params[3] = argv[1];
  96. params[4] = "\"";
  97. exec_cmd(params, 5);
  98. }
  99. while ( do_filechange_check(argv[1], &fileinfo) == REEDIT );
  100. char *params[3];
  101. params[0] = CHECK_IN;
  102. params[1] = argv[1];
  103. params[2] = "\"";
  104. exec_cmd(params, 3);
  105. chown(argv[1], fileinfo.st_uid, fileinfo.st_gid);
  106. return ( 0 );
  107. }
  108. void
  109. do_filecheck(
  110. char *filename
  111. )
  112. {
  113. char *rcspath;
  114. int fname_p;
  115. int str_ln = strlen(filename) - 1;
  116. for (fname_p = str_ln; fname_p > 0 && filename[fname_p] != '/'; fname_p--)
  117. {
  118. ;
  119. }
  120. if (fname_p < 1 && filename[0] == '/')
  121. {
  122. rcspath = "/\0";
  123. fname_p++;
  124. }
  125. else if (fname_p < 1)
  126. {
  127. rcspath = "./\0";
  128. }
  129. else
  130. {
  131. rcspath = new char[fname_p + 1];
  132. strncpy(rcspath, filename, ++fname_p);
  133. }
  134. struct stat *filecheck = new struct stat;
  135. memset(filecheck, -1, sizeof ( struct stat ));
  136. stat(rcspath, filecheck);
  137. if (!(( filecheck->st_mode - S_IFDIR | S_IWUSR )
  138. == filecheck->st_mode - S_IFDIR ))
  139. {
  140. fprintf(stderr, "Error: No write permission for \"%s\"\n", rcspath);
  141. exit(1);
  142. }
  143. int rcsdir_exists = 0, rcsfile_exists = 0;
  144. filecheck->st_size = -1;
  145. memset(filecheck, -1, sizeof ( struct stat ));
  146. char *FULLRCSPATH;
  147. {
  148. char *params[2];
  149. params[0] = rcspath;
  150. params[1] = RCSDIR;
  151. FULLRCSPATH = exec_cmd(params, 2, 1);
  152. }
  153. stat(FULLRCSPATH, filecheck);
  154. if (S_ISDIR(filecheck->st_mode))
  155. {
  156. rcsdir_exists = 1;
  157. if (rcsdir_exists)
  158. {
  159. char *param[3];
  160. param[0] = FULLRCSPATH;
  161. param[1] = filename + fname_p;
  162. param[2] = RCSEXT;
  163. memset(filecheck, -1, sizeof ( struct stat ));
  164. char *fname = exec_cmd(param, 3, 1);
  165. char *qfnme = strstr(fname, "\"");
  166. if (!qfnme)
  167. {
  168. stat(fname, filecheck);
  169. if (S_ISREG(filecheck->st_mode))
  170. {
  171. rcsfile_exists = 1;
  172. }
  173. }
  174. else
  175. {
  176. fprintf(stderr, "Error: Filename may not contain double quotes.\n");
  177. exit(1);
  178. }
  179. }
  180. }
  181. memset(filecheck, -1, sizeof ( struct stat ));
  182. stat(filename, filecheck);
  183. int file_exists = 0;
  184. if (S_ISREG(filecheck->st_mode))
  185. {
  186. file_exists = 1;
  187. }
  188. if (!file_exists && !rcsfile_exists)
  189. {
  190. fprintf(stdout, "Warning: \"%s\" could not be found.\n", filename);
  191. fprintf(stdout, "Create it and check it into RCS? [N] ");
  192. if (dialog() != DG_YES)
  193. {
  194. exit(0);
  195. }
  196. #ifndef DEBUG
  197. FILE *f = fopen(filename, "a");
  198. if (!f)
  199. {
  200. fprintf(stderr, "Error: Failed to create \"%s\": ", filename);
  201. handle_error(-1);
  202. }
  203. else
  204. {
  205. fclose(f);
  206. }
  207. if (!rcsdir_exists)
  208. {
  209. mkdir(FULLRCSPATH, S_IRWXU | S_IRWXG | S_IRWXO);
  210. }
  211. #endif /* ifndef DEBUG */
  212. delete[] FULLRCSPATH;
  213. char *params[3];
  214. params[0] = CHECK_IN;
  215. params[1] = filename;
  216. params[2] = "\"";
  217. exec_cmd(params, 3);
  218. }
  219. else if (!rcsfile_exists && file_exists)
  220. {
  221. fprintf(
  222. stdout,
  223. "\"%s\" is not under RCS control; do initial check in? [N] ",
  224. filename);
  225. if (dialog() != DG_YES)
  226. {
  227. exit(0);
  228. }
  229. #ifndef DEBUG
  230. if (!rcsdir_exists)
  231. {
  232. mkdir(FULLRCSPATH, S_IRWXU | S_IRWXG | S_IRWXO);
  233. }
  234. #endif /* ifndef DEBUG */
  235. delete[] FULLRCSPATH;
  236. char *params[3];
  237. params[0] = CHECK_IN;
  238. params[1] = filename;
  239. params[2] = "\"";
  240. exec_cmd(params, 3);
  241. }
  242. else if (file_exists & rcsfile_exists)
  243. {
  244. memset(filecheck, -1, sizeof ( struct stat ));
  245. stat(filename, filecheck);
  246. if (( filecheck->st_mode - S_IFREG | S_IWUSR )
  247. == filecheck->st_mode - S_IFREG
  248. || ( filecheck->st_mode - S_IFREG | S_IWGRP )
  249. == filecheck->st_mode - S_IFREG
  250. || ( filecheck->st_mode - S_IFREG | S_IWOTH )
  251. == filecheck->st_mode - S_IFREG)
  252. {
  253. fprintf(stderr, "Error: \"%s\" already checked out.\n", filename);
  254. exit(1);
  255. }
  256. }
  257. delete filecheck;
  258. }
  259. int
  260. do_filechange_check(
  261. char *fname,
  262. struct stat *original
  263. )
  264. {
  265. struct stat finfo;
  266. stat(fname, &finfo);
  267. if (finfo.st_mtime != original->st_mtime)
  268. {
  269. fprintf(
  270. stdout,
  271. "File \"%s\" has been modified.\n[A]ccept, [R]e-edit, or [D]iscard? [A] ",
  272. fname);
  273. int key = dialog();
  274. if (key == ABORT)
  275. {
  276. {
  277. char *params[3];
  278. params[0] = UNLOCK;
  279. params[1] = fname;
  280. params[2] = "\"";
  281. exec_cmd(params, 3);
  282. }
  283. remove(fname);
  284. {
  285. char *params[3];
  286. params[0] = CHECK_OUT_R;
  287. params[1] = fname;
  288. params[2] = "\"";
  289. exec_cmd(params, 3);
  290. }
  291. exit(0);
  292. }
  293. else
  294. {
  295. return ( key );
  296. }
  297. }
  298. return ( DG_OTHER );
  299. }
  300. char *
  301. exec_cmd(
  302. char **params,
  303. int len,
  304. int flag
  305. )
  306. {
  307. char *command = new char[D_ARG_MAX];
  308. int buf_ptr = 0;
  309. for (int i = 0; i < len; i++)
  310. {
  311. strncpy(command + buf_ptr, params[i], D_ARG_MAX - buf_ptr);
  312. buf_ptr += strlen(params[i]);
  313. }
  314. strncpy(command + buf_ptr + 1, "\"", D_ARG_MAX - buf_ptr);
  315. #ifndef DEBUG
  316. if (flag)
  317. {
  318. return ( command );
  319. }
  320. handle_error(system(command));
  321. #else /* ifndef DEBUG */
  322. fprintf(stderr, "%s\n", command);
  323. if (flag)
  324. {
  325. return ( command );
  326. }
  327. #endif /* ifndef DEBUG */
  328. delete[] command;
  329. return ((char *)NULL );
  330. }
  331. void
  332. handle_error(
  333. int err
  334. )
  335. {
  336. switch (err)
  337. {
  338. case 127:
  339. fprintf(stderr, "Error: execve call failed\n");
  340. exit(1);
  341. case -1:
  342. fprintf(stderr, "Error: %s\n", strerror(errno));
  343. exit(1);
  344. default:
  345. break;
  346. }
  347. }
  348. int
  349. dialog()
  350. {
  351. char response[10];
  352. fgets(response, 10, stdin);
  353. response[0] = toupper(response[0]);
  354. switch (response[0])
  355. {
  356. case 'D':
  357. return ( ABORT );
  358. case 'R':
  359. return ( REEDIT );
  360. case 'Y':
  361. return ( DG_YES );
  362. case 'N':
  363. return ( DG_NO );
  364. }
  365. return ( DG_OTHER );
  366. }