patch.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * busybox patch applet to handle the unified diff format.
  4. * Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>
  5. *
  6. * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  7. *
  8. * This applet is written to work with patches generated by GNU diff,
  9. * where there is equivalent functionality busybox patch shall behave
  10. * as per GNU patch.
  11. *
  12. * There is a SUSv3 specification for patch, however it looks to be
  13. * incomplete, it doesnt even mention unified diff format.
  14. * http://www.opengroup.org/onlinepubs/007904975/utilities/patch.html
  15. *
  16. * Issues
  17. * - Non-interactive
  18. * - Patches must apply cleanly or the hunk will fail.
  19. * - Reject file isnt saved
  20. * -
  21. */
  22. #include <getopt.h>
  23. #include <string.h>
  24. #include <stdlib.h>
  25. #include <unistd.h>
  26. #include "busybox.h"
  27. static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)
  28. {
  29. unsigned int i = 0;
  30. while (src_stream && (i < lines_count)) {
  31. char *line;
  32. line = xmalloc_fgets(src_stream);
  33. if (line == NULL) {
  34. break;
  35. }
  36. if (fputs(line, dest_stream) == EOF) {
  37. bb_perror_msg_and_die("error writing to new file");
  38. }
  39. free(line);
  40. i++;
  41. }
  42. return i;
  43. }
  44. /* If patch_level is -1 it will remove all directory names
  45. * char *line must be greater than 4 chars
  46. * returns NULL if the file doesnt exist or error
  47. * returns malloc'ed filename
  48. */
  49. static char *extract_filename(char *line, int patch_level)
  50. {
  51. char *temp, *filename_start_ptr = line + 4;
  52. int i;
  53. /* Terminate string at end of source filename */
  54. temp = strchr(filename_start_ptr, '\t');
  55. if (temp) *temp = 0;
  56. /* skip over (patch_level) number of leading directories */
  57. for (i = 0; i < patch_level; i++) {
  58. if(!(temp = strchr(filename_start_ptr, '/'))) break;
  59. filename_start_ptr = temp + 1;
  60. }
  61. return xstrdup(filename_start_ptr);
  62. }
  63. static int file_doesnt_exist(const char *filename)
  64. {
  65. struct stat statbuf;
  66. return stat(filename, &statbuf);
  67. }
  68. int patch_main(int argc, char **argv)
  69. {
  70. int patch_level = -1;
  71. char *patch_line;
  72. int ret;
  73. FILE *patch_file = NULL;
  74. {
  75. char *p, *i;
  76. ret = getopt32(argc, argv, "p:i:", &p, &i);
  77. if (ret & 1)
  78. patch_level = xatol_range(p, -1, USHRT_MAX);
  79. if (ret & 2) {
  80. patch_file = xfopen(i, "r");
  81. } else {
  82. patch_file = stdin;
  83. }
  84. ret = 0;
  85. }
  86. patch_line = xmalloc_fgets(patch_file);
  87. while (patch_line) {
  88. FILE *src_stream;
  89. FILE *dst_stream;
  90. char *original_filename;
  91. char *new_filename;
  92. char *backup_filename;
  93. unsigned int src_cur_line = 1;
  94. unsigned int dest_cur_line = 0;
  95. unsigned int dest_beg_line;
  96. unsigned int bad_hunk_count = 0;
  97. unsigned int hunk_count = 0;
  98. char copy_trailing_lines_flag = 0;
  99. /* Skip everything upto the "---" marker
  100. * No need to parse the lines "Only in <dir>", and "diff <args>"
  101. */
  102. while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {
  103. free(patch_line);
  104. patch_line = xmalloc_fgets(patch_file);
  105. }
  106. /* FIXME: patch_line NULL check?? */
  107. /* Extract the filename used before the patch was generated */
  108. original_filename = extract_filename(patch_line, patch_level);
  109. free(patch_line);
  110. patch_line = xmalloc_fgets(patch_file);
  111. /* FIXME: NULL check?? */
  112. if (strncmp(patch_line, "+++ ", 4) != 0) {
  113. ret = 2;
  114. bb_error_msg("invalid patch");
  115. continue;
  116. }
  117. new_filename = extract_filename(patch_line, patch_level);
  118. free(patch_line);
  119. if (file_doesnt_exist(new_filename)) {
  120. char *line_ptr;
  121. /* Create leading directories */
  122. line_ptr = strrchr(new_filename, '/');
  123. if (line_ptr) {
  124. *line_ptr = '\0';
  125. bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
  126. *line_ptr = '/';
  127. }
  128. dst_stream = xfopen(new_filename, "w+");
  129. backup_filename = NULL;
  130. } else {
  131. backup_filename = xmalloc(strlen(new_filename) + 6);
  132. strcpy(backup_filename, new_filename);
  133. strcat(backup_filename, ".orig");
  134. if (rename(new_filename, backup_filename) == -1) {
  135. bb_perror_msg_and_die("cannot create file %s",
  136. backup_filename);
  137. }
  138. dst_stream = xfopen(new_filename, "w");
  139. }
  140. if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {
  141. src_stream = NULL;
  142. } else {
  143. if (strcmp(original_filename, new_filename) == 0) {
  144. src_stream = xfopen(backup_filename, "r");
  145. } else {
  146. src_stream = xfopen(original_filename, "r");
  147. }
  148. }
  149. printf("patching file %s\n", new_filename);
  150. /* Handle each hunk */
  151. patch_line = xmalloc_fgets(patch_file);
  152. while (patch_line) {
  153. unsigned int count;
  154. unsigned int src_beg_line;
  155. unsigned int unused;
  156. unsigned int hunk_offset_start = 0;
  157. int hunk_error = 0;
  158. /* This bit should be improved */
  159. if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&
  160. (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&
  161. (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {
  162. /* No more hunks for this file */
  163. break;
  164. }
  165. free(patch_line);
  166. hunk_count++;
  167. if (src_beg_line && dest_beg_line) {
  168. /* Copy unmodified lines upto start of hunk */
  169. /* src_beg_line will be 0 if its a new file */
  170. count = src_beg_line - src_cur_line;
  171. if (copy_lines(src_stream, dst_stream, count) != count) {
  172. bb_error_msg_and_die("bad src file");
  173. }
  174. src_cur_line += count;
  175. dest_cur_line += count;
  176. copy_trailing_lines_flag = 1;
  177. }
  178. hunk_offset_start = src_cur_line;
  179. while ((patch_line = xmalloc_fgets(patch_file)) != NULL) {
  180. if ((*patch_line == '-') || (*patch_line == ' ')) {
  181. char *src_line = NULL;
  182. if (src_stream) {
  183. src_line = xmalloc_fgets(src_stream);
  184. if (!src_line) {
  185. hunk_error++;
  186. break;
  187. } else {
  188. src_cur_line++;
  189. }
  190. if (strcmp(src_line, patch_line + 1) != 0) {
  191. bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start);
  192. hunk_error++;
  193. free(patch_line);
  194. break;
  195. }
  196. free(src_line);
  197. }
  198. if (*patch_line == ' ') {
  199. fputs(patch_line + 1, dst_stream);
  200. dest_cur_line++;
  201. }
  202. } else if (*patch_line == '+') {
  203. fputs(patch_line + 1, dst_stream);
  204. dest_cur_line++;
  205. } else {
  206. break;
  207. }
  208. free(patch_line);
  209. }
  210. if (hunk_error) {
  211. bad_hunk_count++;
  212. }
  213. }
  214. /* Cleanup last patched file */
  215. if (copy_trailing_lines_flag) {
  216. copy_lines(src_stream, dst_stream, -1);
  217. }
  218. if (src_stream) {
  219. fclose(src_stream);
  220. }
  221. if (dst_stream) {
  222. fclose(dst_stream);
  223. }
  224. if (bad_hunk_count) {
  225. if (!ret) {
  226. ret = 1;
  227. }
  228. bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);
  229. } else {
  230. /* It worked, we can remove the backup */
  231. if (backup_filename) {
  232. unlink(backup_filename);
  233. }
  234. if ((dest_cur_line == 0) || (dest_beg_line == 0)) {
  235. /* The new patched file is empty, remove it */
  236. if (unlink(new_filename) == -1) {
  237. bb_perror_msg_and_die("cannot remove file %s", new_filename);
  238. }
  239. if (unlink(original_filename) == -1) {
  240. bb_perror_msg_and_die("cannot remove original file %s", new_filename);
  241. }
  242. }
  243. }
  244. }
  245. /* 0 = SUCCESS
  246. * 1 = Some hunks failed
  247. * 2 = More serious problems
  248. */
  249. return ret;
  250. }