Skip to content

Commit aedb7cb

Browse files
committed
initial commit
1 parent 771a02c commit aedb7cb

File tree

5 files changed

+206
-0
lines changed

5 files changed

+206
-0
lines changed

.gitignore

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
*.csproj*
2+
*.sln
3+
bin/*
4+
obj/*
5+
*.config
6+
*.md
7+
LICENSE
8+
*.xcf
9+
*.png

Program.cs

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
using System;
2+
using System.Text.RegularExpressions;
3+
using System.Text;
4+
using System.IO;
5+
6+
namespace bmpurge
7+
{
8+
enum Modes {
9+
BEFORE,
10+
AFTER,
11+
BETWEEN,
12+
NONE
13+
}
14+
15+
class Program
16+
{
17+
static long ToUnixTime(DateTime date) {
18+
DateTime date_univ = date.ToUniversalTime();
19+
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
20+
TimeSpan diff = date_univ - epoch;
21+
return (long)Math.Floor(diff.TotalSeconds);
22+
}
23+
24+
public static void Main(string[] args)
25+
{
26+
if(args.Length < 3) {
27+
Console.WriteLine("Invalid number of arguments: expecting 2 or more!");
28+
return;
29+
}
30+
String path = null;
31+
32+
for(int i = 0; i < args.Length; i++) {
33+
if(File.Exists(args[i])) {
34+
path = args[i];
35+
break;
36+
}
37+
}
38+
if(String.IsNullOrEmpty(path)) {
39+
Console.WriteLine("Invalid argument: please specify an existing file!");
40+
return;
41+
}
42+
43+
bool before = false;
44+
bool after = false;
45+
String before_str = null;
46+
String after_str = null;
47+
Modes mode = Modes.NONE;
48+
for(int i = 0; i < args.Length; i++) {
49+
args[i] = args[i].Trim();
50+
51+
if(
52+
(
53+
(
54+
(args[i] == "-before" && !before) || (args[i] == "-after" && !after) // if arg is before/after and no before/after arg has been found yet
55+
) && (
56+
args.Length <= (i + 1) // but if there is no index after the arg
57+
||
58+
(args[i+1] == "-before" || args[i+1] == "-after") // or if the next arg is after/before instead of a timestamp
59+
)
60+
)
61+
) {
62+
// then -> invalid arg: no timestamp
63+
Console.WriteLine("Invalid argument: no time stamp specified!");
64+
return;
65+
}
66+
// if arg is before but before has already been found
67+
if((args[i] == "-before" && before) || (args[i] == "-after" && after)) {
68+
// then skip
69+
continue;
70+
}
71+
72+
if(args[i] == "-before") {
73+
before = true;
74+
before_str = args[i+1];
75+
mode = Modes.BEFORE;
76+
} else if(args[i] == "-after") {
77+
after = true;
78+
after_str = args[i+1];
79+
mode = Modes.AFTER;
80+
}
81+
}
82+
if(before && after) {
83+
mode = Modes.BETWEEN;
84+
}
85+
if(!before && !after) {
86+
Console.WriteLine("Invalid argument: missing arguments!");
87+
return;
88+
}
89+
90+
// first index: default before date: 1/1/1970
91+
// second index: default after date: 1/1/2500
92+
// if mistakenly not read from args[] then nothing will be deleted
93+
// since no bookmarks should have been created before 01 Jan 1970 or after 01 Jan 2500
94+
long[] dates = {0,16725225600};
95+
96+
if(before) {
97+
DateTime beforeT;
98+
if(String.IsNullOrEmpty(before_str) || !DateTime.TryParse(before_str, out beforeT)) {
99+
Console.WriteLine("Invalid argument: could not read 'before' timestamp!");
100+
return;
101+
}
102+
103+
dates[0] = ToUnixTime(new DateTime(beforeT.Ticks, DateTimeKind.Local));
104+
}
105+
106+
if(after) {
107+
DateTime afterT;
108+
if(String.IsNullOrEmpty(after_str) || !DateTime.TryParse(after_str, out afterT)) {
109+
Console.WriteLine("Invalid argument: could not read 'after' timestamp!");
110+
return;
111+
}
112+
113+
dates[1] = ToUnixTime(new DateTime(afterT.Ticks, DateTimeKind.Local));
114+
}
115+
116+
// if document isn't a netscape bookmarks file, quit
117+
String file_content = File.ReadAllText(path, Encoding.UTF8);
118+
String result = file_content;
119+
if(file_content.Length == 0 || !file_content.Contains("<!DOCTYPE NETSCAPE-Bookmark-file-1>")) {
120+
Console.WriteLine("Invalid argument: file is empty or invalid.");
121+
return;
122+
}
123+
124+
// regex (unescaped): /<a[^>^<]*add_date="(?'date'\d*)"[^>^<]*>[^>^<]*<\/a>/gmis
125+
Regex bookmark_regex = new Regex("\\s*<dt><a[^>^<]*add_date=\"(?'date'\\d*)\"[^>^<]*>(?'title'[^>^<]*)<\\/a>\\s", RegexOptions.IgnoreCase | RegexOptions.Singleline);
126+
MatchCollection matches = bookmark_regex.Matches(file_content);
127+
if(matches.Count == 0) {
128+
Console.WriteLine("No results found!");
129+
return;
130+
}
131+
132+
foreach(Match m in matches) {
133+
if(!m.Groups["date"].Success || !m.Groups["title"].Success)
134+
continue;
135+
136+
long date = Int64.Parse(m.Groups["date"].Value);
137+
138+
switch(mode) {
139+
case Modes.AFTER:
140+
if(date > dates[1]) {
141+
result = result.Replace(m.Value, "");
142+
Console.WriteLine("REMOVED: {0}", m.Groups["title"].Value);
143+
}
144+
break;
145+
case Modes.BEFORE:
146+
if(date < dates[0]) {
147+
result = result.Replace(m.Value, "");
148+
Console.WriteLine("REMOVED: {0}", m.Groups["title"].Value);
149+
}
150+
break;
151+
case Modes.BETWEEN:
152+
if(date > dates[1] && date < dates[0]) {
153+
result = result.Replace(m.Value, "");
154+
Console.WriteLine("REMOVED: {0}", m.Groups["title"].Value);
155+
}
156+
break;
157+
case Modes.NONE:
158+
Console.WriteLine("An unknown error occured.");
159+
break;
160+
}
161+
}
162+
File.WriteAllText(path, result, Encoding.UTF8);
163+
return;
164+
}
165+
}
166+
}

Properties/AssemblyInfo.cs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#region Using directives
2+
3+
using System;
4+
using System.Reflection;
5+
using System.Runtime.InteropServices;
6+
7+
#endregion
8+
9+
// General Information about an assembly is controlled through the following
10+
// set of attributes. Change these attribute values to modify the information
11+
// associated with an assembly.
12+
[assembly: AssemblyTitle("bmpurge")]
13+
[assembly: AssemblyDescription("")]
14+
[assembly: AssemblyConfiguration("")]
15+
[assembly: AssemblyCompany("mxamber")]
16+
[assembly: AssemblyProduct("bmpurge")]
17+
[assembly: AssemblyCopyright("mxamber 2019")]
18+
[assembly: AssemblyTrademark("")]
19+
[assembly: AssemblyCulture("")]
20+
21+
// This sets the default COM visibility of types in the assembly to invisible.
22+
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
23+
[assembly: ComVisible(false)]
24+
25+
// The assembly version has following format :
26+
//
27+
// Major.Minor.Build.Revision
28+
//
29+
// You can specify all the values or you can use the default the Revision and
30+
// Build Numbers by using the '*' as shown below:
31+
[assembly: AssemblyVersion("1.0.*")]

assets/icon_128.ico

10.1 KB
Binary file not shown.

assets/icon_16.ico

318 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)