init
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.vscode
|
||||
|
||||
# production
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
12
CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(CMoe-Counter C)
|
||||
SET(CMAKE_BUILD_TYPE "Release")
|
||||
|
||||
include_directories("/usr/local/include")
|
||||
link_directories("/usr/local/lib")
|
||||
|
||||
add_executable(cmoe cmoe.c)
|
||||
|
||||
target_link_libraries(cmoe spb)
|
||||
|
||||
INSTALL(TARGETS cmoe RUNTIME DESTINATION bin)
|
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Moe-counter
|
||||
|
||||
多种风格可选的萌萌计数器
|
||||
|
||||
![Moe-counter](https://count.getloli.com/get/@Moe-counter.github)
|
||||
|
||||
<details>
|
||||
<summary>More theme</summary>
|
||||
|
||||
##### moebooru(mb)
|
||||
![moebooru](https://count.getloli.com/get/@demo?theme=moebooru)
|
||||
|
||||
##### rule34(r34)
|
||||
![Rule34](https://count.getloli.com/get/@demo?theme=rule34)
|
||||
|
||||
##### gelbooru(gb)
|
||||
![Gelbooru](https://count.getloli.com/get/@demo?theme=gelbooru)</details>
|
||||
|
||||
## Demo
|
||||
[https://count.getloli.com](https://count.getloli.com)
|
||||
|
||||
## Usage
|
||||
### Install [simple-http-server](https://github.com/fumiama/simple-http-server)
|
||||
Follow the instructions in [this repo](https://github.com/fumiama/simple-http-server) to install it.
|
||||
### Install CMoe
|
||||
```bash
|
||||
git clone https://github.com/fumiama/CMoe-Counter.git
|
||||
cd CMoe-Counter
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
```
|
||||
### Copy files
|
||||
1. Copy `favicon.ico`, `index.html`, `style.css` and the compiled binary `cmoe` into the same folder.
|
||||
2. Run `simple-http-server` by the command
|
||||
```bash
|
||||
simple-http-server -d port folder
|
||||
```
|
||||
### Enjoy it!
|
||||
|
||||
## API
|
||||
The API of CMoe is simpler than [Moe-counter](https://github.com/journey-ad/Moe-counter)
|
||||
### Register
|
||||
`token` is defined in `cmoe.h`
|
||||
```
|
||||
http://yourdomain/cmoe?name=yourname®=token
|
||||
```
|
||||
### Refer
|
||||
```
|
||||
http://yourdomain/cmoe?name=yourname(&theme=mb/mbh/gb/gbh/r34)
|
||||
```
|
||||
That's all.
|
||||
## Credits
|
||||
* [Moe-counter](https://github.com/journey-ad/Moe-counter)
|
||||
* [moebooru](https://github.com/moebooru/moebooru)
|
||||
* rule34.xxx NSFW
|
||||
* gelbooru.com NSFW
|
||||
* [Icons8](https://icons8.com/icons/set/star)
|
BIN
assets/favicon.ico
Normal file
After Width: | Height: | Size: 37 KiB |
117
assets/index.html
Normal file
@ -0,0 +1,117 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Moe Counter!</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/light.min.css"/>
|
||||
<link rel="stylesheet" href="style.css"/>
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async="async" src="https://www.googletagmanager.com/gtag/js?id=G-2RLWN5JXRL"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag('js', new Date());
|
||||
|
||||
gtag('config', 'G-2RLWN5JXRL');
|
||||
|
||||
function _evt_push(type, category, label) {
|
||||
gtag('event', type, {
|
||||
'event_category': category,
|
||||
'event_label': label
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h3>How to use:</h3>
|
||||
<h5>SVG address</h5>
|
||||
<code>https://count.getloli.com/get/@:name</code>
|
||||
<h5>Img tag</h5>
|
||||
<code><img src="https://count.getloli.com/get/@:name" alt=":name" /></code>
|
||||
<h5>Markdown</h5>
|
||||
<code>![:name](https://count.getloli.com/get/@:name)</code>
|
||||
<h3>
|
||||
eg:<img src="https://count.getloli.com/get/@index" alt="Moe Count!"/>
|
||||
</h3>
|
||||
<i>
|
||||
Data can access by anyone, please<span style="color: #ff4500;">DO NOT</span>
|
||||
enter personal information
|
||||
</i>
|
||||
<details>
|
||||
<summary style="display: inline-block;" onclick="_evt_push("click", "normal", "more_theme")">
|
||||
<h3 style="display: inline-block; cursor: pointer;">More theme</h3>
|
||||
</summary>
|
||||
<p style="margin: 0;">
|
||||
Just use the query parameters <code>theme</code>
|
||||
, like this: <code>https://count.getloli.com/get/@:name?theme=moebooru</code>
|
||||
</p>
|
||||
<h5>moebooru</h5>
|
||||
<img src="https://count.getloli.com/get/@demo?theme=moebooru" alt="Moebooru"/>
|
||||
<h5>moebooru-h</h5>
|
||||
<img src="https://count.getloli.com/get/@demo?theme=moebooru-h" alt="Moebooru-Hentai"/>
|
||||
<h5>rule34</h5>
|
||||
<img src="https://count.getloli.com/get/@demo?theme=rule34" alt="Rule34"/>
|
||||
<h5>gelbooru</h5>
|
||||
<img src="https://count.getloli.com/get/@demo?theme=gelbooru" alt="Gelbooru"/>
|
||||
<h5>gelbooru-h</h5>
|
||||
<img src="https://count.getloli.com/get/@demo?theme=gelbooru-h" alt="Gelbooru-Hentai"/>
|
||||
</details>
|
||||
<h3>Credits</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://repl.it/" target="_blank" rel="nofollow">repl.it</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/moebooru/moebooru" target="_blank" rel="nofollow">moebooru</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:alert("!!! NSFW LINK !!!\nPlease enter the url manually")">rule34.xxx</a>
|
||||
NSFW
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:alert("!!! NSFW LINK !!!\nPlease enter the url manually")">gelbooru.com</a>
|
||||
NSFW
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://icons8.com/icons/set/star" target="_blank" rel="nofollow">Icons8</a>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>Tool</h3>
|
||||
<div class="tool">
|
||||
<code>https://count.getloli.com/get/@</code>
|
||||
<input id="name" type="text" placeholder=":name" style="display: inline-block; width: 80px; height: 1.4em; line-height: 1.4em; margin: 0 4px; vertical-align: middle;"/>
|
||||
<code>?theme=</code>
|
||||
<select id="theme" style="display: inline-block; height: 1.6em; line-height: 1.6em; font-size: 14px; margin: 0 4px; padding: 0 4px; vertical-align: middle;">
|
||||
<option value="moebooru">moebooru</option>
|
||||
<option value="moebooru-h">moebooru-h</option>
|
||||
<option value="rule34">rule34</option>
|
||||
<option value="gelbooru">gelbooru</option>
|
||||
<option value="gelbooru-h">gelbooru-h</option>
|
||||
</select>
|
||||
<button id="get" style="margin: 10px 0;" onclick="_evt_push("click", "normal", "get_counter")">Get</button>
|
||||
<img id="result" style="display: block;"/>
|
||||
<script>
|
||||
var btn = document.getElementById('get')
|
||||
, img = document.getElementById('result')
|
||||
|
||||
btn.addEventListener('click', function() {
|
||||
var name = document.getElementById('name')
|
||||
, themeEl = document.getElementById('theme')
|
||||
var text = name.value ? name.value.trim() : ''
|
||||
var theme = themeEl.value || 'moebooru'
|
||||
if (!text) {
|
||||
alert('Please input counter name.')
|
||||
return
|
||||
}
|
||||
|
||||
img.src = 'https://count.getloli.com/get/@' + text + '?theme=' + theme
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
<p class="copy">
|
||||
<a href="https://github.com/journey-ad/Moe-counter" target="_blank" onclick="_evt_push("click", "normal", "go_github")">source code</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
5
assets/style.css
Normal file
@ -0,0 +1,5 @@
|
||||
@media screen and (max-width: 900px) {
|
||||
iframe {
|
||||
display: none;
|
||||
}
|
||||
}
|
BIN
assets/theme/gelbooru-h/0.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/theme/gelbooru-h/1.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/theme/gelbooru-h/2.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/theme/gelbooru-h/3.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/theme/gelbooru-h/4.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/theme/gelbooru-h/5.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/theme/gelbooru-h/6.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/theme/gelbooru-h/7.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/theme/gelbooru-h/8.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/theme/gelbooru-h/9.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/theme/gelbooru/0.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/theme/gelbooru/1.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/theme/gelbooru/2.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/theme/gelbooru/3.gif
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/theme/gelbooru/4.gif
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/theme/gelbooru/5.gif
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/theme/gelbooru/6.gif
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/theme/gelbooru/7.gif
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/theme/gelbooru/8.gif
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/theme/gelbooru/9.gif
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/theme/moebooru-h/0.png
Normal file
After Width: | Height: | Size: 680 B |
BIN
assets/theme/moebooru-h/1.png
Normal file
After Width: | Height: | Size: 769 B |
BIN
assets/theme/moebooru-h/2.png
Normal file
After Width: | Height: | Size: 855 B |
BIN
assets/theme/moebooru-h/3.png
Normal file
After Width: | Height: | Size: 775 B |
BIN
assets/theme/moebooru-h/4.png
Normal file
After Width: | Height: | Size: 819 B |
BIN
assets/theme/moebooru-h/5.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/theme/moebooru-h/6.png
Normal file
After Width: | Height: | Size: 795 B |
BIN
assets/theme/moebooru-h/7.png
Normal file
After Width: | Height: | Size: 1012 B |
BIN
assets/theme/moebooru-h/8.png
Normal file
After Width: | Height: | Size: 913 B |
BIN
assets/theme/moebooru-h/9.png
Normal file
After Width: | Height: | Size: 826 B |
BIN
assets/theme/moebooru/0.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/theme/moebooru/1.gif
Normal file
After Width: | Height: | Size: 922 B |
BIN
assets/theme/moebooru/2.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/theme/moebooru/3.gif
Normal file
After Width: | Height: | Size: 974 B |
BIN
assets/theme/moebooru/4.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/theme/moebooru/5.gif
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/theme/moebooru/6.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/theme/moebooru/7.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/theme/moebooru/8.gif
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/theme/moebooru/9.gif
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/theme/rule34/0.gif
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/theme/rule34/1.gif
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
assets/theme/rule34/2.gif
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
assets/theme/rule34/3.gif
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/theme/rule34/4.gif
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/theme/rule34/5.gif
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/theme/rule34/6.gif
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
assets/theme/rule34/7.gif
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
assets/theme/rule34/8.gif
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/theme/rule34/9.gif
Normal file
After Width: | Height: | Size: 5.0 KiB |
210
cmoe.c
Normal file
@ -0,0 +1,210 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <simple_protobuf.h>
|
||||
#include "cmoe.h"
|
||||
|
||||
static uint32_t* items_len;
|
||||
static COUNTER counter;
|
||||
|
||||
#define ADD_HERDER(h)\
|
||||
strcpy(buf + offset, (h));\
|
||||
offset += sizeof((h)) - 1;
|
||||
#define ADD_HERDER_PARAM(h, p)\
|
||||
sprintf(buf + offset, (h), (p));\
|
||||
offset += strlen(buf + offset);
|
||||
#define EXTNM_IS_NOT(name) (strcmp(filepath+extpos, name))
|
||||
|
||||
static void headers(u_int32_t content_len, const char* content_type, int no_cache) {
|
||||
char buf[1024];
|
||||
u_int32_t offset = 0;
|
||||
|
||||
ADD_HERDER(HTTP200 SERVER_STRING);
|
||||
if(no_cache) ADD_HERDER(CACHE_CTRL);
|
||||
ADD_HERDER_PARAM(CONTENT_TYPE, content_type);
|
||||
ADD_HERDER_PARAM(CONTENT_LEN "\r\n", content_len);
|
||||
fwrite(buf, offset, 1, stdout);
|
||||
}
|
||||
|
||||
static void http_error(char* type, char* msg) {
|
||||
fprintf(stdout, type, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static char* get_arg(char* query) {
|
||||
u_int32_t len = 0;
|
||||
while(query[len] && query[len] != '&') len++;
|
||||
if(len > 0) {
|
||||
char* name = malloc(len+1);
|
||||
memcpy(name, query, len);
|
||||
name[len] = 0;
|
||||
return name;
|
||||
} else return NULL;
|
||||
}
|
||||
|
||||
static int del_user(FILE* fp, SIMPLE_PB* spb) {
|
||||
COUNTER *d = (COUNTER *)spb->target;
|
||||
uint32_t next = ftell(fp);
|
||||
uint32_t this = next - spb->real_len;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
uint32_t end = ftell(fp);
|
||||
if(next == end) {
|
||||
return ftruncate(fileno(fp), end - spb->real_len);
|
||||
} else {
|
||||
uint32_t cap = end - next;
|
||||
char *data = malloc(cap);
|
||||
if(data) {
|
||||
fseek(fp, next, SEEK_SET);
|
||||
if(fread(data, cap, 1, fp) == 1) {
|
||||
if(!ftruncate(fileno(fp), end - spb->real_len)){
|
||||
fseek(fp, this, SEEK_SET);
|
||||
if(fwrite(data, cap, 1, fp) == 1) {
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int add_user(char* name, uint32_t count, FILE* fp) {
|
||||
counter.count = count;
|
||||
strncpy(counter.name, name, COUNTER_NAME_LEN-1);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
return !set_pb(fp, items_len, sizeof(COUNTER), &counter);
|
||||
}
|
||||
|
||||
static uint32_t get_content_len(int isbig, uint16_t* len_type, char* cntstr) {
|
||||
uint32_t len = sizeof(svg_small) - 1
|
||||
+ (sizeof(img_slot_front) + sizeof(img_slot_rear) - 2) * 10
|
||||
+ 16 + isbig
|
||||
+ sizeof(svg_tail) - 1;
|
||||
for(int i = 0; i < 10; i++) {
|
||||
len += len_type[cntstr[i] - '0'];
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#define has_next(fp, ch) ((ch=getc(fp)),(feof(fp)?0:(ungetc(ch,fp),1)))
|
||||
#define set_type(name, t, l) if(!strcmp(theme, name)) {\
|
||||
theme_type = t;\
|
||||
len_type = l;\
|
||||
}
|
||||
static void return_count(char* name, char* theme) {
|
||||
FILE* fp = fopen(DATFILE, "rb+");
|
||||
if(!fp) fp = fopen(DATFILE, "wb+");
|
||||
if(fp) {
|
||||
int ch, exist = 0;
|
||||
while(has_next(fp, ch)) {
|
||||
SIMPLE_PB *spb = get_pb(fp);
|
||||
COUNTER *d = (COUNTER *)spb->target;
|
||||
if (!strcmp(name, d->name)) {
|
||||
if(del_user(fp, spb)) http_error(HTTP500, "Unable to Delete Old Data.");
|
||||
else {
|
||||
if (add_user(d->name, d->count + 1, fp)) http_error(HTTP500, "Add User Error.");
|
||||
else {
|
||||
fflush(fp);
|
||||
char cntstr[11];
|
||||
sprintf(cntstr, "%010u", d->count);
|
||||
int isbig = 0;
|
||||
char** theme_type = mb;
|
||||
uint16_t* len_type = mbl;
|
||||
if(theme) {
|
||||
set_type("mbh", mbh, mbhl) else
|
||||
set_type("r34", r34, r34l) else
|
||||
set_type("gb", gb, gbl) else
|
||||
set_type("gbh", gbh, gbhl)
|
||||
isbig = (theme_type == gb || theme_type == gbh);
|
||||
}
|
||||
int w, h;
|
||||
char *head;
|
||||
if(isbig) {
|
||||
w = W_BIG;
|
||||
h = H_BIG;
|
||||
head = svg_big;
|
||||
}
|
||||
else {
|
||||
w = W_SMALL;
|
||||
h = H_SMALL;
|
||||
head = svg_small;
|
||||
}
|
||||
headers(get_content_len(isbig, len_type, cntstr), "image/svg+xml", 1);
|
||||
fwrite(head, sizeof(svg_small)-1, 1, stdout);
|
||||
for(int i = 0; i < 10; i++) {
|
||||
printf(img_slot_front, w * i, w, h);
|
||||
int n = cntstr[i] - '0';
|
||||
fwrite(theme_type[n], len_type[n], 1, stdout);
|
||||
printf(img_slot_rear);
|
||||
}
|
||||
fwrite(svg_tail, sizeof(svg_tail)-1, 1, stdout);
|
||||
}
|
||||
free(spb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else free(spb);
|
||||
}
|
||||
fclose(fp);
|
||||
http_error(HTTP404, "No Such User.");
|
||||
} else http_error(HTTP500, "Open File Error.");
|
||||
}
|
||||
|
||||
static int name_exist(char* name) {
|
||||
FILE* fp = fopen(DATFILE, "rb+");
|
||||
if(!fp) fp = fopen(DATFILE, "wb+");
|
||||
if(fp) {
|
||||
int ch, exist = 0;
|
||||
while(has_next(fp, ch)) {
|
||||
SIMPLE_PB *spb = get_pb(fp);
|
||||
COUNTER *d = (COUNTER *)spb->target;
|
||||
if (!strcmp(name, d->name)) {
|
||||
return 1;
|
||||
}
|
||||
else free(spb);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
} else http_error(HTTP500, "Open File Error.");
|
||||
}
|
||||
|
||||
#define QS (argv[2])
|
||||
//Usage: cmoe method query_string
|
||||
int main(int argc, char **argv) {
|
||||
if(argc == 3) {
|
||||
u_int32_t data_len = 0;
|
||||
char* name = strstr(QS, "name=");
|
||||
items_len = align_struct(sizeof(COUNTER), 2, &counter.name, &counter.count);
|
||||
if(!items_len) http_error(HTTP500, "Align Struct Error.");
|
||||
else if(name) {
|
||||
name = get_arg(name + 5);
|
||||
if(name) {
|
||||
char* theme = strstr(QS, "theme=");
|
||||
if(theme) {
|
||||
theme = get_arg(theme + 6);
|
||||
}
|
||||
char* reg = strstr(QS, "reg=");
|
||||
if(reg) {
|
||||
reg = get_arg(reg + 4);
|
||||
if(reg) {
|
||||
if(strcmp(reg, "fumiama")) http_error(HTTP400, "Token Error.");
|
||||
else if(!name_exist(name)) {
|
||||
FILE* fp = fopen(DATFILE, "ab+");
|
||||
if(fp) {
|
||||
add_user(name, 0, fp);
|
||||
char* msg = "<P>Success.\r\n";
|
||||
headers(strlen(msg), "text/html", 1);
|
||||
fwrite(msg, strlen(msg), 1, stdout);
|
||||
fclose(fp);
|
||||
} else http_error(HTTP500, "Open File Error.");
|
||||
} else http_error(HTTP400, "Name Exist.");
|
||||
} else http_error(HTTP400, "Null Register Token.");
|
||||
} else return_count(name, theme);
|
||||
} else http_error(HTTP400, "Null Name Argument.");
|
||||
} else http_error(HTTP400, "Name Argument Notfound.");
|
||||
} else http_error(HTTP500, "Argument Count Error.");
|
||||
return 0;
|
||||
}
|