• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

CLI interface to medialist (fossil mirror)


Commit MetaInfo

修订版7645bcaf4f5391ffb2694b8e15240ae12dff85fc (tree)
时间2021-11-21 19:09:36
作者mio <stigma@disr...>
Commitermio

Log Message

Share the code for adding missing headers between add.d and update.d (now located in util.d). These are the only places where the code should be required.

FossilOrigin-Name: 46ca890bccd96877f1e48144414e435bf8ea446de1e032f133b38df6312fb958

更改概述

差异

--- a/add.d
+++ b/add.d
@@ -59,16 +59,16 @@ import util;
5959 _log("list '" ~ listName ~ "' doesn't exist.", true);
6060 return false;
6161 }
62-
62+
6363 // no status or progress.
6464 if (args.length < 3)
6565 {
6666 return addItem(listFilePath, title);
6767 }
68-
68+
6969 string status = "UNKNOWN";
7070 string progress = "??/??";
71-
71+
7272 getopt(args,
7373 "status|s", "Set the initial status for the new item", &status,
7474 "progress|p", "Set the initial progress for the new item", &progress);
@@ -78,7 +78,7 @@ import util;
7878 writefln("Added %s to %s (status: %s, progress: %s)", title, listName, status, progress);
7979 return true;
8080 }
81-
81+
8282 return false;
8383 }
8484
@@ -86,115 +86,14 @@ private bool addItem(string listFilePath, string itemName,
8686 string status = "UNKNOWN", string progress = "??/??")
8787 {
8888 File listFile = File(listFilePath, "a+");
89-
90- string[] headers = retrieveListHeaders(&listFile);
91- if (headers is null)
92- {
93- return false;
94- }
95- addMissingHeaders(headers, &listFile);
96- auto headersAndPositions = parseMLHeader(headers);
97- writeNewItem(&listFile, headersAndPositions, itemName, status, progress);
98-
99- return true;
100-}
101-
102-private string[] retrieveListHeaders(File* listFile)
103-{
104- string line;
105-
106- while ((line = listFile.readln()) !is null)
107- {
108- if ('#' != line[0])
109- {
110- return line.strip().split('\t');
111- }
112- }
113-
114- return null;
115-}
116-
117-private void addMissingHeaders(ref string[] fileHeaders, File* listFile)
118-{
119- import std.algorithm.searching : canFind;
120-
121- if (false == canFind(fileHeaders, "start_date"))
122- {
123- addHeader(listFile, "start_date");
124- fileHeaders ~= "start_date";
125- }
126-
127- if (false == canFind(fileHeaders, "end_date"))
128- {
129- addHeader(listFile, "end_date");
130- fileHeaders ~= "end_date";
131- }
132-
133- if (false == canFind(fileHeaders, "last_updated"))
134- {
135- addHeader(listFile, "last_updated");
136- fileHeaders ~= "last_updated";
137- }
138-}
139-
140-private
141-void addHeader(File* f, string newHeader) @trusted
142-in
143-{
144- assert(f.isOpen == true);
145- assert(newHeader !is null);
146-}
147-out
148-{
149- assert(f.isOpen == true);
150-}
151-do
152-{
153- import std.file : rename, remove, tempDir;
154- import std.path : buildPath, baseName;
155-
156- immutable origPath = f.name;
157- immutable tempPath = buildPath(tempDir(), baseName(origPath));
158-
159- File tempFile = File(tempPath, "w+");
160-
161- string line = "";
162- bool pastHeader = false;
163-
164- f.rewind();
165-
166- while ((line = f.readln()) !is null)
167- {
168- if (false == pastHeader && '#' != line[0])
169- {
170- pastHeader = true;
171- tempFile.writefln("%s\t%s", line.strip, newHeader);
172- }
173- else
174- {
175- /* line may not have a newline character */
176- tempFile.writeln(line.strip);
177- }
178- }
179-
180- f.close();
181-
182- remove(origPath);
18389
184- /* some operating systems don't like to copy from the tempdir? */
185- f.open(origPath, "a+");
186- tempFile.rewind();
90+ string[] headers = retrieveListHeaders (&listFile);
91+ addMissingHeaders (headers, &listFile);
18792
188- while ((line = tempFile.readln()) !is null)
189- {
190- f.write(line);
191- }
192-
193- /* rewind because we need to re-read the headers */
194- f.rewind();
93+ auto headersAndPositions = parseMLHeader(headers);
94+ writeNewItem(&listFile, headersAndPositions, itemName, status, progress);
19595
196- tempFile.close();
197- remove(tempPath);
96+ return true;
19897 }
19998
20099 private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] headersAndPositions,
@@ -203,9 +102,9 @@ private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] h
203102 import std.datetime.date : Date;
204103 import std.datetime.systime : Clock;
205104 import std.string : toLower;
206-
105+
207106 size_t previousIndent = 0;
208-
107+
209108 immutable currentDT = Clock.currTime();
210109 immutable currentDateString = Date(currentDT.year, currentDT.month,
211110 currentDT.day).toISOExtString();
@@ -215,7 +114,7 @@ private void writeNewItem(File* listFile, HeaderPairType[NUMBER_OF_ML_HEADERS] h
215114 /* unset header -- no more headers possible */
216115 if (headerPair[0] == 0 && headerPair[1] is null)
217116 break;
218-
117+
219118 listFile.replicateIndent(headerPair, &previousIndent);
220119
221120 switch (headerPair[1])
@@ -262,7 +161,7 @@ private void replicateIndent(File* listFile, HeaderPairType headerPair, size_t*
262161 import core.stdc.stdio : fprintf;
263162 import core.stdc.stdio : cstderr = stderr;
264163 import core.stdc.stdlib : malloc, free;
265-
164+
266165 // Can't add NULL byte to D string, so allocate a temp string and free it.
267166 char* progname = cast(char*)malloc(char.sizeof * programName.length);
268167 scope(exit) free(progname);
@@ -279,3 +178,29 @@ options:
279178 -p, --progress the initial progress of the new item (default: ??/??)
280179 -s, --status the initial status of the new item (default: UNKNOWN)\n", progname);
281180 }
181+
182+
183+/* Testing pre-0.2 files with the addHeaders */
184+unittest
185+{
186+ import std.file : remove;
187+ import std.stdio : File;
188+
189+ enum listName = "unittest-add-pre-0.2";
190+ string line;
191+
192+ auto listFile = File (listName ~ ".tsv", "w+");
193+ scope (exit) remove(listName ~ ".tsv");
194+
195+ listFile.writeln("TITLE\tPROGRESS\tSTATUS");
196+ listFile.close();
197+
198+ /* Shouldn't crash */
199+ handle_add (listName, [listName, "Item 1", "-p", "??/??", "-s", "PLAN-TO-READ"], ".");
200+
201+ listFile.open(listName ~ ".tsv", "r");
202+ listFile.rewind();
203+ line = listFile.readln();
204+ assert ("TITLE\tPROGRESS\tSTATUS\tstart_date\tend_date\tlast_updated\n" == line, "'" ~ line ~ "'");
205+}
206+
--- a/update.d
+++ b/update.d
@@ -110,6 +110,13 @@ handle_update(string program_name, string[] args, string data_dir)
110110 bool read_header = false;
111111 Tuple!(size_t, string)[NUMBER_OF_ML_HEADERS] headers;
112112
113+ /* Add missing headers */
114+ {
115+ File listFile = File(filename, "a+");
116+ string[] headers_ = retrieveListHeaders(&listFile);
117+ addMissingHeaders(headers_, &listFile);
118+ }
119+
113120 /*
114121 * line_number represents the line number in file.
115122 * index initially represents a matching ID number
--- a/util.d
+++ b/util.d
@@ -18,6 +18,7 @@
1818 */
1919 module util;
2020
21+import std.stdio : File;
2122 import std.typecons : Tuple, tuple;
2223
2324 alias HeaderPairType = Tuple!(size_t, string);
@@ -183,3 +184,118 @@ parseMLHeader(string[] headerSections)
183184
184185 return sections;
185186 }
187+
188+public string[]
189+retrieveListHeaders (File* listFile)
190+{
191+ import std.string : strip, split;
192+
193+ string line = null;
194+ string[] fileHeaders;
195+
196+ listFile.rewind ();
197+
198+ while ((line = listFile.readln()) !is null)
199+ {
200+ if ('#' != line[0])
201+ {
202+ fileHeaders = line.strip().split('\t');
203+ break;
204+ }
205+ }
206+
207+ return fileHeaders;
208+}
209+
210+public void
211+addMissingHeaders(ref string[] fileHeaders, File* listFile)
212+{
213+ import core.stdc.stdio : SEEK_END;
214+ import std.algorithm.searching : canFind;
215+
216+ /* Version 0.2 */
217+ if (false == canFind(fileHeaders, "start_date"))
218+ {
219+ addHeader(listFile, "start_date");
220+ fileHeaders ~= "start_date";
221+ }
222+
223+ if (false == canFind(fileHeaders, "end_date"))
224+ {
225+ addHeader(listFile, "end_date");
226+ fileHeaders ~= "end_date";
227+ }
228+
229+ if (false == canFind(fileHeaders, "last_updated"))
230+ {
231+ addHeader(listFile, "last_updated");
232+ fileHeaders ~= "last_updated";
233+ }
234+
235+ listFile.seek(0, SEEK_END);
236+}
237+
238+@trusted private void
239+addHeader(File* f, string newHeader)
240+in
241+{
242+ assert (f.isOpen == true, "f.isOpen == true (in)");
243+ assert (newHeader !is null, "newHeader !is null");
244+}
245+out
246+{
247+ assert (f.isOpen == true, "f.isOpen == true (out)");
248+}
249+do
250+{
251+ import std.file : remove, tempDir;
252+ import std.path : buildPath, baseName;
253+ import std.string : strip;
254+
255+ immutable originalPath = f.name;
256+ immutable tempPath = buildPath(tempDir, baseName(originalPath));
257+
258+ File tempFile = File(tempPath, "w+");
259+
260+ string line = "";
261+ bool pastHeader = false;
262+
263+ /* Make sure we're at the start of the file. */
264+ f.rewind ();
265+
266+ while ((line = f.readln()) !is null)
267+ {
268+ if (false == pastHeader && '#' != line[0])
269+ {
270+ pastHeader = true;
271+ tempFile.writefln ("%s\t%s", strip (line), newHeader);
272+ }
273+ else
274+ {
275+ /* strip + writeln since the previous one may not have had the newline */
276+ tempFile.writeln (strip (line));
277+ }
278+ }
279+
280+ f.close ();
281+
282+ /*
283+ * Since the temporary directory *could* be mounted differently (e.g. remotely)
284+ * some systems don't like to copy from the temporary directory.
285+ */
286+ remove (originalPath);
287+
288+ f.open (originalPath, "w+");
289+ tempFile.rewind ();
290+
291+ while ((line = tempFile.readln()) !is null)
292+ {
293+ f.write (line);
294+ }
295+
296+ /* Rewind since we need to re-parse the headers */
297+ f.rewind ();
298+
299+ tempFile.close();
300+ remove (tempPath);
301+}