summaryrefslogtreecommitdiff
blob: c195a1e5cb7c7a6729f06e6bac293bdd827aa37f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
--- tdl-1.5.2.orig/main.c
+++ tdl-1.5.2/main.c
@@ -80,7 +80,7 @@
   return;
 }
 /*}}}*/
-static volatile void unlock_and_exit(int code)/*{{{*/
+static void unlock_and_exit(int code)/*{{{*/
 {
   unlock_database();
   exit(code);
@@ -237,22 +237,91 @@
 
 }
 /*}}}*/
-static void rename_database(char *path)/*{{{*/
+static mode_t get_mode(const char *path); /* prototype */
+/*}}}*/
+static int copy_file_contents(char *pathsrc, char *pathdest) {
+  int src, dest;
+  ssize_t rdsize = 1;
+  char buf[4096];
+
+  src = open(pathsrc, O_RDONLY);
+  if (src == -1) {
+    perror("warning, couldn't open database");
+    return 0;
+  }
+  dest = open(pathdest, O_WRONLY | O_CREAT, get_mode(pathsrc));
+  if (dest == -1) {
+    perror("warning, couldn't open/create backup database");
+    close(src);
+    return 0;
+  }
+  if (ftruncate(dest,0) != 0) {
+    perror("warning, couldn't truncate backup database");
+    close(src);
+    close(dest);
+    return 0;
+  }
+  lseek(src,0,SEEK_SET);
+  lseek(dest,0,SEEK_SET);
+  while (rdsize > 0) {
+    rdsize = read(src, buf, 4096);
+    if (rdsize == -1) {
+      perror("warning, error reading database");
+      close(src);
+      close(dest);
+      return 0;
+    }
+    if (rdsize > 0) {
+      if (write(dest, buf, rdsize) != rdsize) {
+	perror("warning, error writing to backup database");
+	close(src);
+	close(dest);
+	return 0;
+      }
+    }
+  }
+  close(src);
+  close(dest);
+  return 1;
+}
+/*}}}*/
+static int path_is_symlink(char *path) {
+  int i;
+  struct stat s;
+  i = lstat(path, &s);
+  if ((i == 0) && (S_ISLNK(s.st_mode))) {
+    return 1; /* is a symlink */
+  }
+  return 0; /* not a symlink */
+}
+/*}}}*/
+static int rename_database(char *path)/*{{{*/
 {
-  int len;
+  /* the rename_database function returns 1 if database or/and
+   * database backup file are symlinks; otherwise returns 0 */
+  int len, symlinks;
   char *pathbak;
-
+  
   len = strlen(path);
   pathbak = new_array(char, len + 5);
   strcpy(pathbak, path);
   strcat(pathbak, ".bak");
-  if (rename(path, pathbak) < 0) {
-    if (is_noisy) {
-      perror("warning, couldn't save backup database:");
+
+  symlinks = path_is_symlink(path) | path_is_symlink(pathbak);
+
+  if (symlinks) {
+    if (access(path,F_OK) == 0) {
+      copy_file_contents(path, pathbak);
+    }
+  } else {
+    if (rename(path, pathbak) < 0) {
+      if (is_noisy) {
+	perror("warning, couldn't save backup database:");
+      }
     }
   }
   free(pathbak);
-  return;
+  return symlinks;
 } 
 /*}}}*/
 static char *executable_name(char *argv0)/*{{{*/
@@ -315,7 +384,7 @@
 /*}}}*/
 static void save_database(char *path)/*{{{*/
 {
-  FILE *out;
+  FILE *out = NULL;
   int out_fd;
   mode_t database_mode;
   if (is_loaded && currently_dirty) {
@@ -324,20 +393,34 @@
     /* The next line only used to happen if the command wasn't 'create'.
      * However, it should quietly fail for create, where the existing database
      * doesn't exist */
-    rename_database(path);
-
-    /* Open database this way so that the permissions from the existing
-       database can be duplicated onto the new one in way free of race
-       conditions. */
-    out_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, database_mode);
-    if (out_fd < 0) {
-      fprintf(stderr, "Could not open new database %s for writing : %s\n",
-              path, strerror(errno));
-      unlock_and_exit(1);
+    if (rename_database(path) == 0) {
+      /* database is a regular file */
+      /* Open database this way so that the permissions from the existing
+	 database can be duplicated onto the new one in way free of race
+	 conditions. */
+      out_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, database_mode);
+      if (out_fd < 0) {
+	fprintf(stderr, "Could not open new database %s for writing : %s\n",
+		path, strerror(errno));
+	unlock_and_exit(1);
+      }
     } else {
-      /* Normal case */
-      out = fdopen(out_fd, "wb");
+      /* database and/or backup database are symlinks */
+      /* we should truncate existing file and write its contents */
+      out_fd = open(path, O_WRONLY | O_CREAT, database_mode);
+      if (out_fd < 0) {
+	fprintf(stderr, "Could not open database %s for writing : %s\n",
+		path, strerror(errno));
+	unlock_and_exit(1);
+      } else {
+	/* Normal case */
+	if (ftruncate(out_fd, 0) != 0) {
+	  perror("warning, couldn't truncate database:");
+	  unlock_and_exit(1);
+	}
+      }
     }
+    out = fdopen(out_fd, "wb");
     if (!out) {
       fprintf(stderr, "Cannot open database %s for writing\n", path);
       unlock_and_exit(1);
@@ -728,6 +811,11 @@
 
     if (!is_loaded && cmds[index].load_db) {
       load_database(current_database_path);
+      if (is_interactive && (!is_loaded)) {
+	fprintf(stderr, "error: could not open database. please create a "
+	        "database with 'tdl create' before using this tdl command\n");
+        unlock_and_exit(-1);
+      }
     }
 
     pp = is_tdl ? (p + 1) : p;