Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/editors/patch.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 1  Line 1 
1  /* vi: set sw=4 ts=4: */  /* vi: set sw=4 ts=4: */
2  /*  /*
3   *  busybox patch applet to handle the unified diff format.   *  busybox patch applet to handle the unified diff format.
4   *  Copyright (C) 2003 Glenn McGrath <bug1@iinet.net.au>   *  Copyright (C) 2003 Glenn McGrath
5   *   *
6   *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.   *  Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
7   *   *
# Line 19  Line 19 
19   *   - Reject file isnt saved   *   - Reject file isnt saved
20   */   */
21    
22  #include <getopt.h>  #include "libbb.h"
 #include <string.h>  
 #include <stdlib.h>  
 #include <unistd.h>  
 #include "busybox.h"  
23    
24  static unsigned int copy_lines(FILE *src_stream, FILE *dest_stream, const unsigned int lines_count)  static unsigned copy_lines(FILE *src_stream, FILE *dst_stream, unsigned lines_count)
25  {  {
26   unsigned int i = 0;   while (src_stream && lines_count) {
   
  while (src_stream && (i < lines_count)) {  
27   char *line;   char *line;
28   line = xmalloc_fgets(src_stream);   line = xmalloc_fgets(src_stream);
29   if (line == NULL) {   if (line == NULL) {
30   break;   break;
31   }   }
32   if (fputs(line, dest_stream) == EOF) {   if (fputs(line, dst_stream) == EOF) {
33   bb_perror_msg_and_die("error writing to new file");   bb_perror_msg_and_die("error writing to new file");
34   }   }
35   free(line);   free(line);
36     lines_count--;
  i++;  
37   }   }
38   return i;   return lines_count;
39  }  }
40    
41  /* If patch_level is -1 it will remove all directory names  /* If patch_level is -1 it will remove all directory names
42   * char *line must be greater than 4 chars   * char *line must be greater than 4 chars
43   * returns NULL if the file doesnt exist or error   * returns NULL if the file doesnt exist or error
44   * returns malloc'ed filename   * returns malloc'ed filename
45     * NB: frees 1st argument!
46   */   */
47    static char *extract_filename(char *line, int patch_level, const char *pat)
 static char *extract_filename(char *line, int patch_level)  
48  {  {
49   char *temp, *filename_start_ptr = line + 4;   char *temp = NULL, *filename_start_ptr = line + 4;
  int i;  
50    
51   /* Terminate string at end of source filename */   if (strncmp(line, pat, 4) == 0) {
52   temp = strchr(filename_start_ptr, '\t');   /* Terminate string at end of source filename */
53   if (temp) *temp = 0;   line[strcspn(line, "\t\n\r")] = '\0';
54    
55   /* skip over (patch_level) number of leading directories */   /* Skip over (patch_level) number of leading directories */
56   for (i = 0; i < patch_level; i++) {   while (patch_level--) {
57   if(!(temp = strchr(filename_start_ptr, '/'))) break;   temp = strchr(filename_start_ptr, '/');
58   filename_start_ptr = temp + 1;   if (!temp)
59     break;
60     filename_start_ptr = temp + 1;
61     }
62     temp = xstrdup(filename_start_ptr);
63   }   }
64     free(line);
65   return xstrdup(filename_start_ptr);   return temp;
66  }  }
67    
68  static int file_doesnt_exist(const char *filename)  int patch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
69    int patch_main(int argc UNUSED_PARAM, char **argv)
70  {  {
71   struct stat statbuf;   struct stat saved_stat;
  return stat(filename, &statbuf);  
 }  
   
 int patch_main(int argc, char **argv)  
 {  
  int patch_level = -1;  
72   char *patch_line;   char *patch_line;
73   int ret;   FILE *patch_file;
74   FILE *patch_file = NULL;   int patch_level;
75     int ret = 0;
76     char plus = '+';
77    
78     xfunc_error_retval = 2;
79   {   {
80   char *p, *i;   const char *p = "-1";
81   ret = getopt32(argc, argv, "p:i:", &p, &i);   const char *i = "-"; /* compat */
82   if (ret & 1)   if (getopt32(argv, "p:i:R", &p, &i) & 4)
83   patch_level = xatol_range(p, -1, USHRT_MAX);   plus = '-';
84   if (ret & 2) {   patch_level = xatoi(p); /* can be negative! */
85   patch_file = xfopen(i, "r");   patch_file = xfopen_stdin(i);
  } else {  
  patch_file = stdin;  
  }  
  ret = 0;  
86   }   }
87    
88   patch_line = xmalloc_fgets(patch_file);   patch_line = xmalloc_fgetline(patch_file);
89   while (patch_line) {   while (patch_line) {
90   FILE *src_stream;   FILE *src_stream;
91   FILE *dst_stream;   FILE *dst_stream;
92   char *original_filename;   //char *old_filename;
93   char *new_filename;   char *new_filename;
94   char *backup_filename;   char *backup_filename;
95   unsigned int src_cur_line = 1;   unsigned src_cur_line = 1;
96   unsigned int dest_cur_line = 0;   unsigned dst_cur_line = 0;
97   unsigned int dest_beg_line;   unsigned dst_beg_line;
98   unsigned int bad_hunk_count = 0;   unsigned bad_hunk_count = 0;
99   unsigned int hunk_count = 0;   unsigned hunk_count = 0;
100   char copy_trailing_lines_flag = 0;   smallint copy_trailing_lines_flag = 0;
101    
102   /* Skip everything upto the "---" marker   /* Skip everything upto the "---" marker
103   * No need to parse the lines "Only in <dir>", and "diff <args>"   * No need to parse the lines "Only in <dir>", and "diff <args>"
104   */   */
105   while (patch_line && strncmp(patch_line, "--- ", 4) != 0) {   do {
106   free(patch_line);   /* Extract the filename used before the patch was generated */
107   patch_line = xmalloc_fgets(patch_file);   new_filename = extract_filename(patch_line, patch_level, "--- ");
108   }   // was old_filename above
109   /* FIXME: patch_line NULL check?? */   patch_line = xmalloc_fgetline(patch_file);
110     if (!patch_line) goto quit;
111   /* Extract the filename used before the patch was generated */   } while (!new_filename);
112   original_filename = extract_filename(patch_line, patch_level);   free(new_filename); // "source" filename is irrelevant
113   free(patch_line);  
114     new_filename = extract_filename(patch_line, patch_level, "+++ ");
115   patch_line = xmalloc_fgets(patch_file);   if (!new_filename) {
116   /* FIXME: NULL check?? */   bb_error_msg_and_die("invalid patch");
117   if (strncmp(patch_line, "+++ ", 4) != 0) {   }
118   ret = 2;  
119   bb_error_msg("invalid patch");   /* Get access rights from the file to be patched */
120   continue;   if (stat(new_filename, &saved_stat) != 0) {
121   }   char *slash = strrchr(new_filename, '/');
122   new_filename = extract_filename(patch_line, patch_level);   if (slash) {
123   free(patch_line);   /* Create leading directories */
124     *slash = '\0';
  if (file_doesnt_exist(new_filename)) {  
  char *line_ptr;  
  /* Create leading directories */  
  line_ptr = strrchr(new_filename, '/');  
  if (line_ptr) {  
  *line_ptr = '\0';  
125   bb_make_directory(new_filename, -1, FILEUTILS_RECUR);   bb_make_directory(new_filename, -1, FILEUTILS_RECUR);
126   *line_ptr = '/';   *slash = '/';
127   }   }
  dst_stream = xfopen(new_filename, "w+");  
128   backup_filename = NULL;   backup_filename = NULL;
  } else {  
  backup_filename = xmalloc(strlen(new_filename) + 6);  
  strcpy(backup_filename, new_filename);  
  strcat(backup_filename, ".orig");  
  if (rename(new_filename, backup_filename) == -1) {  
  bb_perror_msg_and_die("cannot create file %s",  
  backup_filename);  
  }  
  dst_stream = xfopen(new_filename, "w");  
  }  
   
  if ((backup_filename == NULL) || file_doesnt_exist(original_filename)) {  
129   src_stream = NULL;   src_stream = NULL;
130     saved_stat.st_mode = 0644;
131   } else {   } else {
132   if (strcmp(original_filename, new_filename) == 0) {   backup_filename = xasprintf("%s.orig", new_filename);
133   src_stream = xfopen(backup_filename, "r");   xrename(new_filename, backup_filename);
134   } else {   src_stream = xfopen_for_read(backup_filename);
  src_stream = xfopen(original_filename, "r");  
  }  
135   }   }
136     dst_stream = xfopen_for_write(new_filename);
137     fchmod(fileno(dst_stream), saved_stat.st_mode);
138    
139   printf("patching file %s\n", new_filename);   printf("patching file %s\n", new_filename);
140    
141   /* Handle each hunk */   /* Handle all hunks for this file */
142   patch_line = xmalloc_fgets(patch_file);   patch_line = xmalloc_fgets(patch_file);
143   while (patch_line) {   while (patch_line) {
144   unsigned int count;   unsigned count;
145   unsigned int src_beg_line;   unsigned src_beg_line;
146   unsigned int unused;   unsigned hunk_offset_start;
147   unsigned int hunk_offset_start = 0;   unsigned src_last_line = 1;
148   int hunk_error = 0;   unsigned dst_last_line = 1;
149    
150   /* This bit should be improved */   if ((sscanf(patch_line, "@@ -%d,%d +%d,%d", &src_beg_line, &src_last_line, &dst_beg_line, &dst_last_line) < 3)
151   if ((sscanf(patch_line, "@@ -%d,%d +%d,%d @@", &src_beg_line, &unused, &dest_beg_line, &unused) != 4) &&   && (sscanf(patch_line, "@@ -%d +%d,%d", &src_beg_line, &dst_beg_line, &dst_last_line) < 2)
152   (sscanf(patch_line, "@@ -%d,%d +%d @@", &src_beg_line, &unused, &dest_beg_line) != 3) &&   ) {
  (sscanf(patch_line, "@@ -%d +%d,%d @@", &src_beg_line, &dest_beg_line, &unused) != 3)) {  
153   /* No more hunks for this file */   /* No more hunks for this file */
154   break;   break;
155   }   }
156   free(patch_line);   if (plus != '+') {
157     /* reverse patch */
158     unsigned tmp = src_last_line;
159     src_last_line = dst_last_line;
160     dst_last_line = tmp;
161     tmp = src_beg_line;
162     src_beg_line = dst_beg_line;
163     dst_beg_line = tmp;
164     }
165   hunk_count++;   hunk_count++;
166    
167   if (src_beg_line && dest_beg_line) {   if (src_beg_line && dst_beg_line) {
168   /* Copy unmodified lines upto start of hunk */   /* Copy unmodified lines upto start of hunk */
169   /* src_beg_line will be 0 if its a new file */   /* src_beg_line will be 0 if it's a new file */
170   count = src_beg_line - src_cur_line;   count = src_beg_line - src_cur_line;
171   if (copy_lines(src_stream, dst_stream, count) != count) {   if (copy_lines(src_stream, dst_stream, count)) {
172   bb_error_msg_and_die("bad src file");   bb_error_msg_and_die("bad src file");
173   }   }
174   src_cur_line += count;   src_cur_line += count;
175   dest_cur_line += count;   dst_cur_line += count;
176   copy_trailing_lines_flag = 1;   copy_trailing_lines_flag = 1;
177   }   }
178   hunk_offset_start = src_cur_line;   src_last_line += hunk_offset_start = src_cur_line;
179     dst_last_line += dst_cur_line;
180    
181   while ((patch_line = xmalloc_fgets(patch_file)) != NULL) {   while (1) {
182   if ((*patch_line == '-') || (*patch_line == ' ')) {   free(patch_line);
183     patch_line = xmalloc_fgets(patch_file);
184     if (patch_line == NULL)
185     break; /* EOF */
186     if ((*patch_line != '-') && (*patch_line != '+')
187     && (*patch_line != ' ')
188     ) {
189     break; /* End of hunk */
190     }
191     if (*patch_line != plus) { /* '-' or ' ' */
192   char *src_line = NULL;   char *src_line = NULL;
193     if (src_cur_line == src_last_line)
194     break;
195   if (src_stream) {   if (src_stream) {
196   src_line = xmalloc_fgets(src_stream);   src_line = xmalloc_fgets(src_stream);
197   if (!src_line) {   if (src_line) {
198   hunk_error++;   int diff = strcmp(src_line, patch_line + 1);
  break;  
  } else {  
199   src_cur_line++;   src_cur_line++;
200     free(src_line);
201     if (diff)
202     src_line = NULL;
203   }   }
  if (strcmp(src_line, patch_line + 1) != 0) {  
  bb_error_msg("hunk #%d FAILED at %d", hunk_count, hunk_offset_start);  
  hunk_error++;  
  free(patch_line);  
  /* Probably need to find next hunk, etc... */  
  /* but for now we just bail out */  
  patch_line = NULL;  
  break;  
  }  
  free(src_line);  
204   }   }
205   if (*patch_line == ' ') {   if (!src_line) {
206   fputs(patch_line + 1, dst_stream);   bb_error_msg("hunk #%u FAILED at %u", hunk_count, hunk_offset_start);
207   dest_cur_line++;   bad_hunk_count++;
208     break;
209     }
210     if (*patch_line != ' ') { /* '-' */
211     continue;
212   }   }
  } else if (*patch_line == '+') {  
  fputs(patch_line + 1, dst_stream);  
  dest_cur_line++;  
  } else {  
  break;  
213   }   }
214   free(patch_line);   if (dst_cur_line == dst_last_line)
215   }   break;
216   if (hunk_error) {   fputs(patch_line + 1, dst_stream);
217   bad_hunk_count++;   dst_cur_line++;
218   }   } /* end of while loop handling one hunk */
219   }   } /* end of while loop handling one file */
220    
221   /* Cleanup last patched file */   /* Cleanup last patched file */
222   if (copy_trailing_lines_flag) {   if (copy_trailing_lines_flag) {
223   copy_lines(src_stream, dst_stream, -1);   copy_lines(src_stream, dst_stream, (unsigned)(-1));
224   }   }
225   if (src_stream) {   if (src_stream) {
226   fclose(src_stream);   fclose(src_stream);
227   }   }
228   if (dst_stream) {   fclose(dst_stream);
  fclose(dst_stream);  
  }  
229   if (bad_hunk_count) {   if (bad_hunk_count) {
230   if (!ret) {   ret = 1;
231   ret = 1;   bb_error_msg("%u out of %u hunk FAILED", bad_hunk_count, hunk_count);
  }  
  bb_error_msg("%d out of %d hunk FAILED", bad_hunk_count, hunk_count);  
232   } else {   } else {
233   /* It worked, we can remove the backup */   /* It worked, we can remove the backup */
234   if (backup_filename) {   if (backup_filename) {
235   unlink(backup_filename);   unlink(backup_filename);
236   }   }
237   if ((dest_cur_line == 0) || (dest_beg_line == 0)) {   if ((dst_cur_line == 0) || (dst_beg_line == 0)) {
238   /* The new patched file is empty, remove it */   /* The new patched file is empty, remove it */
239   if (unlink(new_filename) == -1) {   xunlink(new_filename);
240   bb_perror_msg_and_die("cannot remove file %s", new_filename);   // /* old_filename and new_filename may be the same file */
241   }   // unlink(old_filename);
  if (unlink(original_filename) == -1) {  
  bb_perror_msg_and_die("cannot remove original file %s", new_filename);  
  }  
242   }   }
243   }   }
244   }   free(backup_filename);
245     //free(old_filename);
246     free(new_filename);
247     } /* end of "while there are patch lines" */
248     quit:
249   /* 0 = SUCCESS   /* 0 = SUCCESS
250   * 1 = Some hunks failed   * 1 = Some hunks failed
251   * 2 = More serious problems   * 2 = More serious problems (exited earlier)
252   */   */
253   return ret;   return ret;
254  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.816